The Windows Forms DataGrid

The Windows Forms DataGrid

Besides per-record viewing, there is also a need to view many records at one time, providing quick access to the data in one place. With data in this format, it is easy to scan and perform analysis on the information as a whole. Sometimes it is just convenient to browse the data, looking for records that meet specific criteria when the identity of those records is not immediately known. These scenarios describe a good application for the DataGrid control.

The Windows Forms DataGrid is well suited for viewing many records at the same time. It is sophisticated, customizable, and exposes much functionality. At its simplest, the DataGrid may bind to data and display information with minimal effort. Because of its many features, complete coverage of its functionality is not possible here, but the examples presented in this section should offer insight into how to move forward with future customization.

The example in this section contains five separate code files: a launch window, a base class form, and three other forms to demonstrate tasks for working with a DataGrid control. The architecture of this application is illustrated in Figure 16.4.

Figure 16.4. Windows Forms DataGrid binding.

graphics/16fig04.gif

Each item in Figure 16.4 is also shown in Figures 16.5 through Figure 16.8. The launch window (see Figure 16.5) contains a button with event handlers to launch each of the BaseForm derived types. Each of the forms DataGridCombo, DataGridWidth, and DataGridRow inherits BaseForm, which contains a DataGrid that is bound to the Northwind database Customers table.

Figure 16.5. DataGrid Forms launch window (WinDataGrid.cs).

graphics/16fig05.jpg

Figure 16.8. Selecting an entire DataGrid row (DataGridRow.cs).

graphics/16fig08.jpg

Because the other three Windows Forms inherit BaseForm, they also have the same bound DataGrid. All they need to do is add more specific functionality. For example, the DataGridCombo form (see Figure 16.6) adds a ComboBox control to the Country column of the DataGrid.

Figure 16.6. DataGrid with ComboBox column (DataGridCombo.cs).

graphics/16fig06.jpg

The DataGridWidth form (see Figure 16.7) has button controls that allow programmatic modification of the CompanyName column width.

Figure 16.7. DataGrid with program-modifiable column width (DataGridWidth.cs).

graphics/16fig07.jpg

The Windows Forms application in Figure 16.8 shows how to highlight an entire row when any column in that row is selected.

When starting this project, create a Windows Forms Application project. Rename the WinForm file to WinDataGrid, which you will use to add buttons that launch each of the other Windows Forms that will be added to this application. This ensures that the Main method is added to this form, which will be the launch form. The following code shows the event handler for the button that would launch the DataGridCombo example. Similar event handlers should be created to launch other demos:

 private void btnCombo_Click(object sender, System.EventArgs e) {    DataGridCombo dgCombo = new DataGridCombo();    dgCombo.ShowDialog(); } 

As mentioned earlier, the BaseForm class adds a DataGrid that is bound to the Customers table in the Northwind database. This form was set up by adding a DataGrid from the Tool Palette and setting its Dock Property to Fill. Chapter 15 explains how to do this as well as how to set up the BDP components. To bind this DataGrid to the data source, select the DataGrid and locate the DataSource and DataMember properties in the Data section of the Object Inspector. Set the DataSource property to the name of the DataSet, which you should have already created. Then set the DataMember property to Table1. If the Active property of the BdpDataAdapter is set to true, the information from the Customers table should appear in the DataGrid.

Before moving on, remember that BaseForm will be inherited by other forms, so certain controls, such as DataGrid and DataSet, must be visible to derived types. By default, all controls added to the form are added with private modifiers. To change this, set the Modifiers property in the Design section of the Object Inspector for both the DataGrid and the DataSet to Protected. Listing 16.1 contains code for the BaseForm class.

Listing 16.1 A Windows Form with a DataGrid (BaseForm.cs)
 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace WinDataGridDemo {    /// <summary>    /// Summary description for BaseForm.    /// </summary>    public class BaseForm : System.Windows.Forms.Form    {       /// <summary>       /// Required designer variable.       /// </summary>       private System.ComponentModel.Container components = null;       protected System.Windows.Forms.DataGrid dgrCustomers;       private Borland.Data.Provider.BdpConnection bdpConnection1;       private Borland.Data.Provider.BdpCommand bdpSelectCommand1;       private Borland.Data.Provider.BdpCommand bdpInsertCommand1;       private Borland.Data.Provider.BdpCommand bdpUpdateCommand1;       private Borland.Data.Provider.BdpCommand bdpDeleteCommand1;       private Borland.Data.Provider.BdpDataAdapter bdpDataAdapter1;       protected System.Data.DataSet dataSet1;       private System.Data.DataTable Customers;       private System.Data.DataColumn dataColumn1;       private System.Data.DataColumn dataColumn2;       private System.Data.DataColumn dataColumn3;       private System.Data.DataColumn dataColumn4;       private System.Data.DataColumn dataColumn5;       public BaseForm()       {          InitializeComponent();       }       /// <summary>       /// Clean up any resources being used.       /// </summary>       protected override void Dispose (bool disposing)       {          if (disposing)          {             if (components != null)             {                components.Dispose();             }          }          base.Dispose(disposing);       }       #region Windows Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {          // auto-generated code removed       }       #endregion    } } 

Most of the code in Listing 16.2 was automatically generated by the IDE. I modified the namespace because the first file created in this project was called WinDataGrid, its namespace was WinDataGrid, and the class name was WinDataGrid. Having namespace and class names the same is not good style and sometimes causes problems with compiling code that references classes with the same name as their namespace. Because all of the data work is done in BaseForm, derived types don't need to reproduce it and can concentrate on the specific functionality they provide.

In this example, I modified the DataSet table name. To do this, select dataSet1 in the Component Tray, select Tables in the Data section of the Object Inspector, select the button in the Tables value column to bring up the Tables Collection Editor dialog, and select Table1 in the Members column. Change the Name and TableName properties in the property grid to Customers and click the Close button to close the dialog. After the TableName has been changed, the name of the table in the DataSet may be referred to by using the name Customers.

The first form that inherits from BaseForm is called DataGridCombo because it adds a ComboBox control to the Countries column of the DataGrid. It doesn't matter that the DataGrid is implemented by BaseForm because DataGridCombo has access to its members, as long as they were set to protected access or higher. Before creating DataGridCombo, make sure BaseForm is compiled so it will show up as an inheritable form. The DataGridCombo form was created with the Form Inheritance Wizard, which you implement by selecting File, New, Other and selecting the BaseForm icon in the C# Projects\Inheritable Forms folder (see Figure 16.9).

Figure 16.9. Initializing the Inheritable Forms Wizard.

graphics/16fig09.jpg

The basic approach is to determine whether the current column is the right one to use for adding the ComboBox and show it. Some administrative chores need to be handled along the way to make the user interface respond properly, but other than that it is straightforward. Listing 16.2 shows how to add a ComboBox column to a DataGrid.

Listing 16.2 Adding a ComboBox column to a DataGrid (DataGridCombo.cs)
 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace WinDataGridDemo {    /// <summary>    /// Summary description for DataGridCombo.    /// </summary>    public class DataGridCombo : WinDataGridDemo.BaseForm    {       const int CountryCol = 4;       ComboBox cbxCountries = new ComboBox();       /// <summary>       /// Required designer variable.       /// </summary>       private System.ComponentModel.Container components = null;       public DataGridCombo()       {          InitializeComponent();       }       /// <summary>       /// Clean up any resources being used.       /// </summary>       protected override void Dispose (bool disposing)       {          if (disposing)          {             if (components != null)             {                components.Dispose();             }          }          base.Dispose(disposing);       }       #region Windows Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {          ((System.ComponentModel.ISupportInitialize)             (this.dgrCustomers)).BeginInit();          ((System.ComponentModel.ISupportInitialize)             (this.dataSet1)).BeginInit();          //          // dgrCustomers          //          this.dgrCustomers.Name = "dgrCustomers";          this.dgrCustomers.Click += new             System.EventHandler(this.dgrCustomers_Click);          this.dgrCustomers.CurrentCellChanged += new             System.EventHandler(                this.dgrCustomers_CurrentCellChanged);          this.dgrCustomers.Paint += new             System.Windows.Forms.PaintEventHandler(                this.dgrCustomers_Paint);          this.dgrCustomers.Scroll += new             System.EventHandler(this.dgrCustomers_Scroll);          //          // dataSet1          //          this.dataSet1.DataSetName = "NewDataSet";          this.dataSet1.Locale = new             System.Globalization.CultureInfo("en-US");          //          // DataGridCombo          //          this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);          this.ClientSize = new System.Drawing.Size(432, 326);          this.Name = "DataGridCombo";          this.Text = "DataGridCombo";          this.Load += new             System.EventHandler(this.DataGridCombo_Load);          ((System.ComponentModel.ISupportInitialize)              (this.dgrCustomers)).EndInit();          ((System.ComponentModel.ISupportInitialize)              (this.dataSet1)).EndInit();       }       #endregion       private void DataGridCombo_Load(          object sender, System.EventArgs e)       {          cbxCountries.Visible = false;          cbxCountries.Items.Clear();          cbxCountries.TextChanged += new             EventHandler(cbxCountries_TextChanged);          string[] countries = GetCountries();          cbxCountries.Items.AddRange(countries);          dgrCustomers.PreferredRowHeight = cbxCountries.Height;          dgrCustomers.Controls.Add(cbxCountries);       }       private string[] GetCountries()       {          ArrayList countries = new ArrayList();          DataTable customers = dataSet1.Tables["Customers"];          foreach (DataRow row in customers.Rows)          {             countries.Add(row["Country"]);          }          return (string[])countries.ToArray(typeof(string));       }       // load cell with what was selected in combo box       private void cbxCountries_TextChanged(          object sender, System.EventArgs e)       {          DataGridCell currCell = dgrCustomers.CurrentCell;          if (currCell.ColumnNumber == CountryCol)          {             cbxCountries.Visible = false;             dgrCustomers[currCell] = cbxCountries.Text;          }       }       // make sure combo box width is set properly on repaint       private void dgrCustomers_Paint(          object sender, System.Windows.Forms.PaintEventArgs e)       {          if (dgrCustomers.CurrentCell.ColumnNumber == CountryCol)          {             cbxCountries.Width =                dgrCustomers.GetCurrentCellBounds().Width;          }       }       // hides combo box when it's being       // worked on or should not be shown       private void HideComboBox()       {          cbxCountries.Visible = false;          cbxCountries.Width   = 0;       }       // initialize combo box       private void dgrCustomers_CurrentCellChanged(          object sender, System.EventArgs e)       {          HideComboBox();          DataGridCell currCell = dgrCustomers.CurrentCell;          if (currCell.ColumnNumber == CountryCol)          {             Rectangle cellBounds =                dgrCustomers.GetCurrentCellBounds();             cbxCountries.Left    = cellBounds.Left;             cbxCountries.Top     = cellBounds.Top;             cbxCountries.Text    =               (string)dgrCustomers[currCell];             cbxCountries.Visible = true;          }       }       private void dgrCustomers_Click(          object sender, System.EventArgs e)       {          HideComboBox();       }       private void dgrCustomers_Scroll(          object sender, System.EventArgs e)       {          HideComboBox();       }    } } 

The first thing to notice about DataGridCombo in Listing 16.2 is that it inherits from BaseForm. This gives it the same data handling capabilities for free. The ComboBox control, cbxCountries, was manually added to the code, along with a constant value, countryCol, which is used to determine which column the ComboBox will be associated with.

The process starts in the DataGridCombo_Load method, which is called by the Load event of the form when it starts up the first time. This method hooks up the TextChanged event so that the program can get the selected item out of the ComboBox and put it into the DataGrid when a new country is selected. It then gets the list of available countries that a user may select from the ComboBox. The GetCountries routine demonstrates how to iterate through the rows of a given DataTable control to obtain values. This was primarily to demonstrate this technique, and a better implementation would be to make a SELECT DISTINCT query on the Customers table.

The dgrCustomers_Paint method was hooked up via the Events tab for the DataGrid on the DataGridCombo form. It simply adjusts the width of the ComboBox in case the user decides to manually resize the column. When working with the ComboBox, it's better to hide it to prevent screen flickering. That's the purpose of the HideComboBox method. The dgrCustomers_CurrentCellChanged method checks the current column selected against the countryCol and sets up the ComboBox control if this is the Countries column. Whenever a user scrolls or clicks elsewhere on the DataGrid, the ComboBox should be hidden.

If you are following along, this may be a good time to try out this part of the example. Make sure there is a button on the WinDataGrid form that has an event handler to launch this dialog, as explained earlier.

Another example is the DataGridWidth form, which allows programmatic modification of a column width. To get this program working, create a new form by using the Forms Inheritance Wizard, as explained earlier, inheriting from BaseForm. Add a Panel control, set its Dock property to Bottom, and adjust the size just large enough to hold a couple of buttons. Then add two Button controls, name them btnDecrement and btnIncrement, and change their Text properties to "<< Decrement" and "Increment >>", respectively. Double-click each of the buttons to create event handlers, and add event handler code as shown in Listing 16.3.

Listing 16.3 Programmatically Changing DataGrid Column Width (DataGridWidth.cs)
 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace WinDataGridDemo {    /// <summary>    /// Summary description for DataGridWidth.    /// </summary>    public class DataGridWidth : WinDataGridDemo.BaseForm    {       private DataGridTableStyle tblStyle;       private const int AmountIncOrDec = 10;       /// <summary>       /// Required designer variable.       /// </summary>       private System.ComponentModel.Container components = null;       private System.Windows.Forms.Panel panel1;       private System.Windows.Forms.Button btnDecrement;       private System.Windows.Forms.Button btnIncrement;       public DataGridWidth()       {          InitializeComponent();       }       /// <summary>       /// Clean up any resources being used.       /// </summary>       protected override void Dispose (bool disposing)       {          if (disposing)          {             if (components != null)             {                components.Dispose();             }          }          base.Dispose(disposing);       }       #region Windows Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {          this.panel1 = new System.Windows.Forms.Panel();          this.btnIncrement = new System.Windows.Forms.Button();          this.btnDecrement = new System.Windows.Forms.Button();          ((System.ComponentModel.ISupportInitialize)              (this.dgrCustomers)).BeginInit();          ((System.ComponentModel.ISupportInitialize)              (this.dataSet1)).BeginInit();          this.panel1.SuspendLayout();          this.SuspendLayout();          //          // dgrCustomers          //          this.dgrCustomers.Name = "dgrCustomers";          this.dgrCustomers.Size = new System.Drawing.Size(432, 262);          //          // dataSet1          //          this.dataSet1.DataSetName = "NewDataSet";          this.dataSet1.Locale = new             System.Globalization.CultureInfo("en-US");          //          // panel1          //          this.panel1.Controls.Add(this.btnIncrement);          this.panel1.Controls.Add(this.btnDecrement);          this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;          this.panel1.Location = new System.Drawing.Point(0, 262);          this.panel1.Name = "panel1";          this.panel1.Size = new System.Drawing.Size(432, 64);          this.panel1.TabIndex = 1;          //          // btnIncrement          //          this.btnIncrement.Location =             new System.Drawing.Point(240, 24);          this.btnIncrement.Name = "btnIncrement";          this.btnIncrement.Size = new System.Drawing.Size(104, 23);          this.btnIncrement.TabIndex = 1;          this.btnIncrement.Text = "Increment >>";          this.btnIncrement.Click += new             System.EventHandler(this.btnIncrement_Click);          //          // btnDecrement          //          this.btnDecrement.Location =             new System.Drawing.Point(88, 24);          this.btnDecrement.Name = "btnDecrement";          this.btnDecrement.Size = new             System.Drawing.Size(104, 23);          this.btnDecrement.TabIndex = 0;          this.btnDecrement.Text = "<< Decrement";          this.btnDecrement.Click += new             System.EventHandler(this.btnDecrement_Click);          //          // DataGridWidth          //          this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);          this.ClientSize = new System.Drawing.Size(432, 326);          this.Controls.Add(this.panel1);          this.Name = "DataGridWidth";          this.Text = "DataGridWidth";          this.Load += new System.EventHandler(this.DataGridWidth_Load);          this.Controls.SetChildIndex(this.panel1, 0);          this.Controls.SetChildIndex(this.dgrCustomers, 0);          ((System.ComponentModel.ISupportInitialize)              (this.dgrCustomers)).EndInit();          ((System.ComponentModel.ISupportInitialize)              (this.dataSet1)).EndInit();          this.panel1.ResumeLayout(false);          this.ResumeLayout(false);       }       #endregion       private void DataGridWidth_Load(          object sender, System.EventArgs e)       {          tblStyle = new DataGridTableStyle();          tblStyle.MappingName = "Customers";          dgrCustomers.TableStyles.Add(tblStyle);       }       private void btnDecrement_Click(          object sender, System.EventArgs e)       {          tblStyle.GridColumnStyles[0].Width -= AmountIncOrDec;          tblStyle.DataGrid.Refresh();       }       private void btnIncrement_Click(          object sender, System.EventArgs e)       {          tblStyle.GridColumnStyles[0].Width += AmountIncOrDec;          tblStyle.DataGrid.Refresh();       }    } } 

In Listing 16.3, there is a constant at the top of the file, AmountIncOrDec, to control the amount of pixels to change in the column width. The DataGridWidth_Load event handler method initializes a new DataGridTableStyle type and assigns it to the TableStyles collection in the DataGrid. The DataGridTableStyle is a very useful type for modifying the appearance of a DataGrid.

Both the btnDecrement_Click and btnIncrement_Click event handlers modify the Width property of the first column by accessing the GridColumnStyles collection. More information on both the DataGridTableStyles and DataGridColumnStyles types can be found in the .NET Framework SDK documentation, along with a complete list of properties that may be set.

The final example demonstrates how to highlight an entire DataGrid row whenever a cell on that row has been selected. To set up this form, create the DataGridRow form by using the Form Inheritance Wizard and selecting BaseForm, just like the other two forms. With the DataGrid selected, go to the Events tab of the Object Inspector and create a handler for the MouseUp event by double-clicking in the MouseUp property value box. The functionality for selecting a row is to determine if a cell has been selected, as opposed to a caption or other part of the DataGrid, and then select the row. Listing 16.4 shows how to implement this.

Listing 16.4 Selecting a DataGrid Row (DataGridRow.cs)
 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace WinDataGridDemo {    /// <summary>    /// Summary description for DataGridRow.    /// </summary>    public class DataGridRow : WinDataGridDemo.BaseForm    {       /// <summary>      /// Required designer variable.       /// </summary>       private System.ComponentModel.Container components = null;       public DataGridRow()       {          InitializeComponent();       }       /// <summary>       /// Clean up any resources being used.       /// </summary>       protected override void Dispose (bool disposing)       {          if (disposing)          {             if (components != null)             {                components.Dispose();             }          }          base.Dispose(disposing);       }       #region Windows Form Designer generated code       /// <summary>       /// Required method for Designer support - do not modify       /// the contents of this method with the code editor.       /// </summary>       private void InitializeComponent()       {           ((System.ComponentModel.ISupportInitialize)              (this.dgrCustomers)).BeginInit();            ((System.ComponentModel.ISupportInitialize)              (this.dataSet1)).BeginInit();           //           // dgrCustomers           //           this.dgrCustomers.Name = "dgrCustomers";           this.dgrCustomers.MouseUp += new             System.Windows.Forms.MouseEventHandler(                this.dgrCustomers_MouseUp);           //           // dataSet1           //           this.dataSet1.DataSetName = "NewDataSet";           this.dataSet1.Locale = new             System.Globalization.CultureInfo("en-US");           //           // DataGridRow           //           this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);           this.ClientSize = new System.Drawing.Size(432, 326);           this.Name = "DataGridRow";           ((System.ComponentModel.ISupportInitialize)              (this.dgrCustomers)).EndInit();           ((System.ComponentModel.ISupportInitialize)              (this.dataSet1)).EndInit();       }       #endregion       private void dgrCustomers_MouseUp(          object sender, System.Windows.Forms.MouseEventArgs e)       {          DataGrid.HitTestInfo hitTestInfo =             dgrCustomers.HitTest(new Point(e.X, e.Y));          if(hitTestInfo.Type == DataGrid.HitTestType.Cell)          {             dgrCustomers.Select(hitTestInfo.Row);          }       }    } } 

The dgrCustomers_MouseUp event handler in Listing 16.4 obtains a HitTestInfo instance by using the X and Y coordinates as parameters to the HitTest method of the DataGrid. After determining that the mouse click occurred in a DataGrid cell, the method simply calls the DataGrid.Select method. Using this method of hit testing, other types of interaction with a user may be performed when customizing a DataGrid control.



C# Builder KickStart
C# Builder KickStart
ISBN: 672325896
EAN: N/A
Year: 2003
Pages: 165

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net