Professional Documents
Culture Documents
Screen 6.1
Screen 6.1 presents the code that is displayed in our window. Here, we discern a list of 'using'
statements and a minus sign, extending over an entire span of six 'using' statements. Now,
click on the minus sign, and observe your screen change its display, as shown in screen 6.2.
Screen 6.2
To our utter astonishment, all the 'using' statements vanish, and what is remnant is a plus
sign and the term 'using', with a series of dots as residues. Thus, the editor is conscious of how
it has to work with the C# language. It is not merely a word processor, but a word processor
that knows and discerns the C# programming language, better than we do. The words, also
known as keywords, that are a part of the C# language, are depicted in a different color. Also,
every separate entity begins with a minus sign. Now, click on the minus sign in front of class.
Screen 6.3 displays the Code Painter.
Screen 6.3
Clicking on the minus sign of the class, compresses it into a series of dots. This signifies that it
is the class that encapsulates all the code. We can compress any entity in C# and thereby,
remain at the level of detail we are comfortable with. With a small screen, we need to be very
selective about the level of detail that can be displayed. Now, incorporate a button onto the
Form Design and double click on it. The addition of a button, results in a variation in the code
also. The program is shown below.
Form1.cs
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace z10
{
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
this.button1.Location = new System.Drawing.Point(40, 64);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.AddRange(new System.Windows.Forms.Control[]
{ this.button1});
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void button1_Click(object sender, System.EventArgs e)
{
}
}
}
The above program displays the code that is generated after the button is clicked. A large
portion of the code remains the same, as was noticed in the earlier program. Therefore, we
shall desist from harping upon it any longer. Besides, from now onwards, only relevant parts of
the code will be revealed, to avoid redundancy and confusion, and also to conserve paper.
The newly added button in the form is called button1. Therefore, an instance variable is
created with the same name, i.e. button1. The data type is Button, which is a class in the
Forms namespace. Any GUI code that needs to be executed before the window is displayed, is
always placed in the InitializeComponent function.
Any line that originates with the # symbol, is known as a pre-processor directive. The pre-
processor is a program that executes before the C# compiler commences its work. The #region
command displays a minus sign. If we click on it, the region which extends from this sign upto
the #endregion, collapses. The words displayed in the box, remain the same, as shown after the
#region directive. This is amply evident in screen 6.4.
Screen 6.4
The InitializeComponent function contains a large number of statements that deal with the
Button object. First and foremost, an instance of the button object is created, with the help of
the 'new' keyword. The function SuspendLayout, as the name itself suggests, does not execute
any code that deals with placement of controls or layout, until the function ResumeLayout is
called. Invariably, this is the very last function to be called. The Location property is initialized
to the position where the control is placed, and the name is the text displayed on the button.
The Click property is called a 'delegate' in C#, which is associated with a function that gets
called, each time the button is clicked. The name of the function is passed as a parameter to
the constructor of class named EventHandler.
There could be multiple buttons present in a form. Each of them would necessitate a separate
function, which is to be called when the button is clicked. Therefore, the approach adopted by
the programmers for determining the function name, is as follows: The name of the button,
followed by an underscore, followed by the word 'Click'. In this manner, distinct names are
assigned to each of the functions, since the names of the controls are always unique.
All this discussion proves to be inefficacious, unless we display a button on the screen. To
achieve this, the services of the AddRange function, belonging to the Controls object in the
Forms class, are employed. This function accepts an array of the Control datatype, which holds
widgets to be displayed. In this case, there is only a single button; however, in real life, there
could be numerous controls. This function is used to add multiple controls, simultaneously.
Thus, by using the Graphical interface, life becomes a lot more simpler, since we can eschew
the drudgery of manually entering a horde of new changes, embodied in the form. Imagine how
cumbersome it would be to write and re-write the code, each time the button is shifted, even a
little. A new value would have to be assigned to the Location property, every time the button is
moved.
Each time we double click on the Form, we are directed to a function called Form1_Load. This
is due to the fact that the property Load, belonging to the Form class, is assigned this value.
The code in the above function, is executed before the window is displayed, but after the
execution of the function InitializeComponent.
Everything about Visual Studio.Net is dynamic. Any change incorporated in the value of a
property in the Code Painter, gets reflected instantaneously. Locate the property called Text in
the Code Painter, which is shown as:
this.button1.Text = "button1";
Now, change it to the following:
this.button1.Text = "vijay";
Then, click on the Design Tab. Some processing takes place for a while, before the Form is
activated. In the form, the Text property changes to 'vijay' immediately. Now, add the following
line in the function InitializeComponent, just below the Location property:
this.button1.Size = new System.Drawing.Size(72, 80);
The moment we click on the Design Tab, the size of the button undergoes a transformation.
The Form Design and the Properties window also change. In the Properties window, the value
of Size changes immediately. In the Design, delete the button by selecting it, and then, pressing
the Delete key. Switch to the Code Painter, and you will witness that, all references to the
button named button1, have been removed from the code, including the ones entered by us.
However, the function button1_Click, does not get erased. There appears to be a bug in the
Beta copy of the product. We presume that this bug will be fixed in the final copy of the
product.
We have not saved anything so far. We would advise you against changing anything in the
Code Painter. Instead, you may use the Properties Window for this purpose. The C# compiler
couldn't care less about who has actually entered the code. It merely validates the syntax and
creates an exe file. This exe file is then executed, using the F5 option.
The next example relates to the code generated by the Framework when data is displayed from
a database. So, select the Data tab from the toolbox, and bring an OleDbDataAdapter into the
form. In case you are unable to recollect the steps, peruse through Chapter 2 of this book.
Select the data connection of VMUKHI.Northwind.dbo, and insert the following SQL statement:
SELECT CustomerID, CompanyName, ContactName FROM Customers
In the Form, the OleDbDataAdapter control also adds another control called
oleDbConnection1. Switch to the Code Painter and observe the modifications made to the code.
private System.Data.OleDb.OleDbDataAdapter oleDbDataAdapter1;
private System.Data.OleDb.OleDbCommand oleDbSelectCommand1;
private System.Data.OleDb.OleDbCommand oleDbInsertCommand1;
private System.Data.OleDb.OleDbCommand oleDbUpdateCommand1;
private System.Data.OleDb.OleDbCommand oleDbDeleteCommand1;
private System.Data.OleDb.OleDbConnection oleDbConnection1;
At first, six instance variables are created. Apart from the oleDbDataAdapter1 and
oleDbConnection1, four more variables are created, each belonging to the data type
OleDbCommand. Now, expand the region 'Windows Form Designer generated code', and view
the revised code in the InitializeComponent function.
private void InitializeComponent()
{
this.oleDbDataAdapter1 = new System.Data.OleDb.OleDbDataAdapter();
this.oleDbSelectCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbInsertCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbUpdateCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbDeleteCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbConnection1 = new System.Data.OleDb.OleDbConnection();
this.oleDbDataAdapter1.DeleteCommand = this.oleDbDeleteCommand1;
this.oleDbDataAdapter1.InsertCommand = this.oleDbInsertCommand1;
this.oleDbDataAdapter1.SelectCommand = this.oleDbSelectCommand1;
this.oleDbDataAdapter1.TableMappings.AddRange(new System.Data.Common.DataTableMapping[] {
new System.Data.Common.DataTableMapping("Table", "Customers", new
System.Data.Common.DataColumnMapping[] {
new System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"),new
System.Data.Common.DataColumnMapping("CompanyName", "CompanyName"), new
System.Data.Common.DataColumnMapping("ContactName", "ContactName")})});
this.oleDbDataAdapter1.UpdateCommand = this.oleDbUpdateCommand1;
this.oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName, ContactName
FROM Customers";
this.oleDbSelectCommand1.Connection = this.oleDbConnection1;
this.oleDbInsertCommand1.CommandText = "INSERT INTO Customers(CustomerID, CompanyName,
ContactName) VALUES (?, ?, ?);"+
"SELECT CustomerID, CompanyName, ContactName FROM Customers WHERE (CustomerID = ?)";
this.oleDbInsertCommand1.Connection = this.oleDbConnection1;
this.oleDbInsertCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter("CustomerID",
System.Data.OleDb.OleDbType.WChar, 5, System.Data.ParameterDirection.Input, false,
((System.Byte)(0)), ((System.Byte)(0)), "CustomerID", System.Data.DataRowVersion.Current, null));
this.oleDbInsertCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("CompanyName", System.Data.OleDb.OleDbType.VarWChar, 40,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)),
"CompanyName", System.Data.DataRowVersion.Current, null));
this.oleDbInsertCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("ContactName", System.Data.OleDb.OleDbType.VarWChar, 30,
System.Data.ParameterDirection.Input, true, ((System.Byte)(0)), ((System.Byte)(0)),
"ContactName", System.Data.DataRowVersion.Current, null));
this.oleDbInsertCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Select_CustomerID", System.Data.OleDb.OleDbType.WChar, 5,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)), "CustomerID",
System.Data.DataRowVersion.Current, null));
this.oleDbUpdateCommand1.CommandText = @"UPDATE Customers SET CustomerID = ?,
CompanyName = ?, ContactName = ? WHERE (CustomerID = ?) AND (CompanyName = ?) AND
(ContactName = ? OR ? IS NULL AND ContactName IS NULL); SELECT CustomerID, CompanyName,
ContactName FROM Customers WHERE (CustomerID = ?)";
this.oleDbUpdateCommand1.Connection = this.oleDbConnection1;
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("CustomerID", System.Data.OleDb.OleDbType.WChar, 5,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)), "CustomerID",
System.Data.DataRowVersion.Current, null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("CompanyName", System.Data.OleDb.OleDbType.VarWChar, 40,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)),
"CompanyName", System.Data.DataRowVersion.Current, null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("ContactName", System.Data.OleDb.OleDbType.VarWChar, 30,
System.Data.ParameterDirection.Input, true, ((System.Byte)(0)), ((System.Byte)(0)),
"ContactName", System.Data.DataRowVersion.Current, null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Original_CustomerID", System.Data.OleDb.OleDbType.WChar,
5, System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)),
"CustomerID", System.Data.DataRowVersion.Original, null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Original_CompanyName",
System.Data.OleDb.OleDbType.VarWChar, 40, System.Data.ParameterDirection.Input, false,
((System.Byte)(0)), ((System.Byte)(0)), "CompanyName", System.Data.DataRowVersion.Original,
null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Original_ContactName",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, true,
((System.Byte)(0)), ((System.Byte)(0)), "ContactName", System.Data.DataRowVersion.Original,
null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Original_ContactName1",
System.Data.OleDb.OleDbType.VarWChar, 30, System.Data.ParameterDirection.Input, true,
((System.Byte)(0)), ((System.Byte)(0)), "ContactName", System.Data.DataRowVersion.Original,
null));
this.oleDbUpdateCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("Select_CustomerID", System.Data.OleDb.OleDbType.WChar, 5,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)), "CustomerID",
System.Data.DataRowVersion.Current, null));
this.oleDbDeleteCommand1.CommandText = "DELETE FROM Customers WHERE (CustomerID = ?) AND
(CompanyName = ?) AND"+ "(ContactName = ? OR ? IS NULL AND ContactName IS NULL)";
this.oleDbDeleteCommand1.Connection = this.oleDbConnection1;
this.oleDbDeleteCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter("CustomerID",
System.Data.OleDb.OleDbType.WChar, 5, System.Data.ParameterDirection.Input, false,
((System.Byte)(0)), ((System.Byte)(0)), "CustomerID", System.Data.DataRowVersion.Original, null));
this.oleDbDeleteCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("CompanyName", System.Data.OleDb.OleDbType.VarWChar, 40,
System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)),
"CompanyName", System.Data.DataRowVersion.Original, null));
this.oleDbDeleteCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("ContactName", System.Data.OleDb.OleDbType.VarWChar, 30,
System.Data.ParameterDirection.Input, true, ((System.Byte)(0)), ((System.Byte)(0)),
"ContactName", System.Data.DataRowVersion.Original, null));
this.oleDbDeleteCommand1.Parameters.Add(new
System.Data.OleDb.OleDbParameter("ContactName1", System.Data.OleDb.OleDbType.VarWChar, 30,
System.Data.ParameterDirection.Input, true, ((System.Byte)(0)), ((System.Byte)(0)),
"ContactName", System.Data.DataRowVersion.Original, null));
this.oleDbConnection1.ConnectionString = "Provider=SQLOLEDB.1;Persist Security Info=False;User
ID=sa;Initial Catalog=Northw" +
"ind;Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation" +
" ID=VMUKHI;Use Encryption for Data=False;Tag with column collation when possible" +
"=False";
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
}
The code for the function InitializeComponent has been inserted without us having to make
any changes at all. By now, you would have realized, as to why we are so fascinated with
Visual Studio.Net. It generates all the code required for a certain task, without a single error.
Let us spend a little time, trying to construe the above code. As there are six instance variables,
each of them will have a new statement, which will initialize them.
The first variable is of type OleDbDataAdapter class, which is used to fetch data from a
database. Then, we need SQL commands, which will help us in working with the records from
a table in the database. Thus, the 4 property statements of type OleDbCommand,
oleDbSelectCommand1, oleDbInsertCommand1, oleDbUpdateCommand1 and
oleDbDeleteCommand1, represent SQL statements, which are to be used while working with
records.
The columns in our table, require to be mapped to the columns in the data source. In order to
accomplish this, the TableMappings property of type DataTableMappingCollection, is employed.
This class contains a function called AddRange, which accepts an array of DataTableMapping
objects. A DataTableMapping class has a constructor, which accepts three parameters. The
first parameter is a string, which represents a Table. The second parameter is Customers,
which is the name of the table in the dataset, to map to. The third parameter is an array of
Column names.
The column names furnished in the last parameter are represented with the help of a
DataColumnMapping class. The constructor to this class is passed two parameters. The first is
the name of the column in the data source, while the second is the name of the column in the
dataset to map to. We have maintained both of these identical, although they could have
easily varied.
The SQL statement that we have written, generates this AddRange function. It associates or
maps the three chosen columns, with the columns in the Customers table. The
oleDbSelectCommand1 object requires an SQL select statement, which will determine the data
to be retrieved. The CommandText property is therefore, initialized to the Select statement
entered earlier in the Query Builder. The Connection property is initialized to the
OleDbConnection control, for each of the select, update, delete or insert objects.
For inserting data, the SQL statement has a? symbol, or a placeholder to hold text, which is to
be entered at run time. The syntax starts with the reserved word 'Insert into', followed by the
name of the table, followed by the field names, followed by the reserved word Values, and
finally, followed by a set of brackets, containing the actual data. This data can also be the
resultant data of a Select statement. Either of the three, i.e. ? or placeholder or parameter, is
filled with text supplied by the user at runtime. This is done for reasons of efficiency. The Insert
or Select statements are first parsed, and then, verified for various types of errors. This is a
protracted and time-consuming exercise. Therefore, the product of this action is stored for
reuse, thereby speeding things up. Thus, by the use parameters, execution can be speeded up,
since the database merely needs add the data, without checking the syntax. Three parameters
are to be added to the Insert object, since there are three column names, viz. CustomerID,
CompanyName and ContactName. The rules for adding a parameter remain the same, while
implementing all the three parameters.
The Parameters collection takes an OleDbParameter object, whose constructor accepts 10
parameters, which are as follows:
• The first parameter is the name of the parameter, and in our case, it is termed as
CustomerID.
• The second parameter is the data type of the parameter, which is of type OleDbType.
It may be any one of the numerous data types that are available. One such data type is
wchar, which is a series of Unicode characters, ending in a null. It maps to a string data
type in C#.
• The third parameter is the size or width of the column, which is 5 characters long.
• The fourth parameter, of type ParameterDirection, can contain only four values,
which specify whether the parameter is used for input or output; or both; or for the
return value of some code being executed on the server.
• The fifth parameter is a Boolean, which accepts a value of either True or False. In a
database, you can specify whether a field can contain a Null value or not. Null cannot
be equated to zero, since it specifically implies that the field is bereft of any value
whatsoever. A value of False necessitates the presence of a value in this field.
• The sixth parameter stands for 'precision', which indicates the exact number of
decimal places that a number can possibly have.
• The seventh parameter is the 'scale' parameter, which is also responsible for the
number of decimal places that the value can hold.
• The eighth parameter is the name of the source column in the table.
• The ninth parameter is of type DataRowVersion, and specifies the version of the
DataRow object being retrieved. The type Current represents the current or present
value. It can have three other values, viz. Default, Original and Proposed.
• The tenth parameter is Null. It stands for the value of any of the parameters. We may
use any data type, to assign a value to the parameter.
It would be so cumbersome if we had to write the above code by hand, for each of the
parameters in the Insert statement. This is surely a dreary job! Since someone has to do this
job, why not let the computer do it?
The 'update' command necessitates a similar treatment. So, we use the Update statement,
which starts with the reserved word Update, followed by the name of the table i.e. Customers,
and the 'set' keyword, which specifies the fields that need to be updated. There are three
placeholders or ? signs, since three fields have been chosen. The term 'where' is like a filter,
since in its absence, the update statement will update all records in the database. The eight
parameters that need to be annexed for the Update statement, are responsible for the
extremely lengthy code. The Delete statement is considerably more tractable than the code
specified above. It starts with the Delete keyword, followed by the name of the table, and a
'where' condition, which filters the number of records. The four placeholders require four
parameter statements.
Next comes the Connection object, oleDbConenction1. It is this particular object, which
understands how to aptly communicate with a database. The data that specifies how to
communicate with a database, is passed in the ConenctionString property. The Connection
string starts with the word 'provider', with the name of the database that we wish to talk to,
followed by a semi-colon, which acts as a separator. Then, we have the User ID of 'sa', since 'sa'
was entered in the dialog box. Next, we have the Catalog as the name of the database,
NorthWind. We shall explore the others, a little later.
The visible controls get added to the Forms collection, whereas the invisible controls do not.
The only difference between a visible control and an invisible control is that, the former shows
up in the form, while the latter shows up at the bottom. These concepts are divulged only after
the code that is generated, has been deciphered thoroughly. Otherwise, we shall just be
groping in the dark. Thus, we endeavor to understand the code from the absolutely basic
fundamentals.
Next, we generate the Data Set object, using the menu option Data- Generate Dataset, and we
name the Datatset as 'ddd'. The screen 6.5 represents the dataset dialog box.
Screen 6.5
Once this is done, switch to the Code Painter to view the new sets of commands. An instance
variable, called ddd1, gets created with the data type of z10.ddd. This is because, we named
our DataSet as 'ddd', and the dataset object in Visual Studio.Net was named 'ddd1'.
The next thing that we need to do is, to discover where the class ddd has been created. It does
not show up in the current file. We plan to unveil this class mystery, in just a short while.
But for now, we will take a look at the code of the dataset, which was created in the function
InitializeComponent.
private z10.ddd ddd1;
private void InitializeComponent ()
{
this.ddd1 = new z10.ddd();
((System.ComponentModel.ISupportInitialize)(this.ddd1)).BeginInit();
this.ddd1.DataSetName = "ddd";
this.ddd1.Locale = new System.Globalization.CultureInfo("en-US");
this.ddd1.Namespace = "http://www.tempuri.org/ddd.xsd";
}
As always, a new instance is created, and thereafter, a function called BeginInit is called. This
function is present in the interface IsupportInitialize, which explains the use of the cast
operator. This function merely enlightens the whole world with the news, that the object is
about to initialize itself. Unless the program reaches the EndInit function, initialization will not
be completed.
This is akin to fixing a 'Do Not Disturb' sign outside your door. The cast that we have above, is
really unnecessary, since the DataSet class implements the interface. Whenever computer
programs write code, they are extremely specific. The DataSetName is set to 'ddd', since we
opted for this name in the dialog box, during the creation of the dataset. The Locale property is
used to compare strings present in the table. The Namespace property is set to the name of an
xsd file. File ddd.xsd is shown below.
ddd.xsd
<xsd:schema id="ddd" targetNamespace="http://www.tempuri.org/ddd.xsd"
xmlns="http://www.tempuri.org/ddd.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified"
elementFormDefault="qualified">
<xsd:element name="ddd" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="Customers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string" />
<xsd:element name="CompanyName" type="xsd:string" />
<xsd:element name="ContactName" type="xsd:string" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
<xsd:unique name="Constraint1" msdata:PrimaryKey="true">
<xsd:selector xpath=".//Customers" />
<xsd:field xpath="CustomerID" />
</xsd:unique>
</xsd:element>
</xsd:schema>
This file displays a large number of tags, which we shall explicate while discussing XML. The
line of code in which the table name Customers is stored, is the most significant one. Within
the tag sequence, the three field names are present along with their data types. There is also an
attribute called PrimaryKey, which specifies that the CustomerID field is unique. The above
program named Form1.cs, cannot be compiled at this stage, since it does not contain any code
for the ddd class. The only way out is, to create this class from the xsd file. So, we use the xsd
program for this purpose. We run it as follows:
C:\mukhi\z10>xsd /d /n:z10 ddd.xsd
Output
Microsoft (R) Xml Schemas/DataTypes support utility
[Microsoft (R) .NET Framework, Version 1.0.2914.16]
Copyright (C) Microsoft Corp. 1998-2001. All rights reserved.
Writing file 'C:\mukhi\z10\ddd.cs'.
The /d option creates a class named ddd, derived from DataSet. The /n option places all the
code in the namespace z10. Finally, a file called ddd.cs gets created, from which, we have
extracted the initial few lines. Remember that, none of this 10K sized code, has been written by
us.
namespace z10
{
public class ddd : System.Data.DataSet
{
Now that the class ddd is available, compile the Form program, using the command
>csc Form1.cs ddd.cs
The compiler can be passed as many C# program files as you desire. We are not out of the
woods yet, since we have to introduce a DataGrid, which shall display all the data.
The moment we incorporate a DataGrid object, an instance variable named dataGrid1 gets
created.
private System.Windows.Forms.DataGrid dataGrid1;
Double click on the datagrid and you will see the following code in the function
InitializeComponent
this.dataGrid1 = new System.Windows.Forms.DataGrid();
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
this.dataGrid1.DataMember = "";
this.dataGrid1.Location = new System.Drawing.Point(56, 40);
this.dataGrid1.Name = "dataGrid1";
this.dataGrid1.TabIndex = 0;
this.dataGrid1.Navigate += new
System.Windows.Forms.NavigateEventHandler(this.dataGrid1_Navigate);
this.Controls.AddRange(new System.Windows.Forms.Control[] {this.dataGrid1});
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
private void dataGrid1_Navigate(object sender, System.Windows.Forms.NavigateEventArgs ne)
{
}
The Navigate event gets fired, whenever we move into a new table in the data grid, since the
DataMember property is blank by default. The whole world appears to be pre-occupied with
calling the BeginInit and EndInit functions. They all behave in a similar manner.
We finally set DataSource to ddd1 and DataMember to Customers. These are the two most
important properties in the Properties window.
this.dataGrid1.DataSource = this.ddd1;
this.dataGrid1.DataMember = "Customers";
The Fill function is placed in the Form1_Load function before running the program, as the
dataset needs to be filled up with data.
private void Form1_Load(object sender, System.EventArgs e)
{
oleDbDataAdapter1.Fill(ddd1);
}
The next application will selectively retrieve data from the customers table. To be precise, we
will provide a customer number, and instantly, specific details about that customer will be
displayed. But prior to this, we intend to display a single record from the customers table in
the textboxes.
Save all the files, and close the current Solution. Then, click on File - New - Project, and choose
Visual C# Project in the first pane and Windows Application in the second pane. Assign the
name z14 to the project, and locate it in C:\mukhi. Then, click on the OK button. Do ensure
that the Properties and the ToolBox windows are visible.
Click on the Data tab in the toolbox, and then usher-in the OleDbDataAdapter control. This
activates the wizard. On the first screen, simply click on the Next button. To obtain the name of
Data Connection, click on the drop down listbox, and select the one named Northwind
database. Then, click on the Next button, leaving the SQL statements selected; and then, write
the following SQL statement:
SELECT CustomerID, CompanyName, ContactName FROM Customers
Finally, click on the Finish button. The two invisible objects get created in the lower pane. We
are also aware of the fact that, great many lines of code have been generated.
Click on menu Data - Generate Dataset. Change the name of the dataset to ddd, and then,
click on OK. Thereafter, drag-and-drop three textboxes onto the form, from the ToolBox in the
Windows Form tag.
Select the first textbox, and in the Properties Window, click on the plus sign in front of
DataBindings. This will bring up the DataBindings options. Click on the drop down listbox for
the property Text. This will reveal the dataset ddd1, with a plus sign in front of it. A dataset
contains a large number of tables. Hence, we see a plus sign along with it, in the Properties
Window. Click on the plus sign with ddd, to display a list of tables present in it. Since there is
only one table present, we see only the Customers table listed, but with a plus sign. Every table
contains columns. Clicking on the plus sign, will display the three column names contained in
customers. Select CustomerID, as shown in screen 6.6.
Screen 6.6
By selecting the column CustomerID for the first textbox, we confine the display of all values
related to CustomerID, to this particular textbox alone. Follow the same procedure to select
CompanyName for the second textbox, and finally, to select ContactName for the third textbox.
After effecting this, double click on the form, to enter the Fill function in the function
Form1_Load.
oleDbDataAdapter1.Fill(ddd1);
Then, press F5 to run the application.
Screen 6.7
In screen 6.7, the three fields of the first record are displayed in the three textboxes.
Let us now introduce a button from the Windows Form tab so that the record pointer moves to
the next record when clicked on the button. Change the text property in the Properties window
to Next, and then, double click on the button. You will be transported to the function
button1_Click. Now, enter the following line of code:
BindingContext[ddd1, "Customers"].Position += 1;
The Form class has a property called BindingContext, of data type BindingContext. This
property, in turn, has an Indexer that accepts two parameters. As was explained in the
previous chapter, an Indexer looks like an array, walks like an array and talks like an array;
but it is not an array!
The two parameters that this Indexer accepts are:
• The name of the dataset, i.e. ddd1.
• The name of the table, which is within that DataSet, i.e. Customers.
This indexer returns a BindingManagerBase object, which has a property called Position. This
property contains the current record pointer.
The record pointer or active record, is an abstract entity, which marks a single row of data in
the dataset that becomes visible to everyone. The value contained in the Position property
activates the specific row in the dataset. The initial value starts at zero.
Each time we click on the button, the value is incremented by 1, using the += syntax.
Screen 6.8
The above code can be re-written effortlessly, as follows:
BindingManagerBase a;
a= BindingContext[ddd1, "Customers"];
a.Position = a.Position + 1;
This code is a simplified version of the earlier one. However, it is infested with a small bug. If
the value of the Position parameter exceeds the number of records, no error is generated. In
such a situation, the last record will be displayed. So, how do we redress this problem?
Introduce another button, and change the Text property to Prev. Then, double click on this
button, and in the Code Generator for the function button2_Click, add the following line of
code.
BindingContext[ddd1, "Customers"].Position -= 1;
Now, on running the application, two buttons will be visible, as shown in screen 6.9.
Screen 6.9
Each time we click on the Next button, the record pointer moves to the next record; whereas,
every time we click on the Prev button, it moves to the previous record.
Thus, each and every record can be accessed sequentially, in the forwards or backwards
direction.
Now, we add one more button, which moves the record pointer directly to the first record.
Change its text to 'First', and then, double click on it, to add the following line of code:
BindingContext[ddd1, "Customers"].Position = 0;
Once this is done, build the project, and press F5 to run the program. Click on the Next button
a couple of times, and then, click on the button labeled First. On doing so, the record pointer
jumps directly to the first record, and it gets displayed. This occurs on account of the fact that,
when the Position property is set to zero, the first row in the dataset becomes the active row.
Finally, select a button in the Toolbox, and change its Text property to Last. Then, add the
following lines of code to it:
int i;
i = BindingContext[ddd1, "Customers"].Count;
BindingContext[ddd1, "Customers"].Position = i-1;
The BindingManagerBase class has a member called Count, which provides the total count of
rows or record sets available in the dataset. The variable i is set to the value of this property.
Then, the Position parameter is initialized to one less than the value of i, since the index of the
Position parameter begins with a value of zero. Thus, if we have 10 rows, the value of the count
property will be 10. The index value of the first record will be 0, and that of the last record will
be 9.
There is yet another feature remaining, which is a textbox that reveals the current position, or
the current record number, in the dataset. Each time the Position property changes, the new
record number must be displayed. In fact, the position changes whenever any button is
clicked. The code required to display the new record numbers, remains unchanged. So,
instead of repeating the same code four times, once for each of the four different buttons, we
shall enclose this code within a function. This function will then be called by each of the four
buttons.
So, select the textbox from the Toolbox window. The textbox will be named textBox4.
Then, enter the function abc, as shown below, after the last function. The four buttons shall
now call this function.
Screen 6.10
void abc()
{
int cnt,pos;
cnt = BindingContext[ddd1, "Customers"].Count;
pos = BindingContext[ddd1, "Customers"].Position;
pos = pos+1;
textBox4.Text = "Record No " + pos + " of Record " + cnt;
}
Also, call the abc function after the Fill function in Form_Load.
private void Form1_Load(object sender, System.EventArgs e)
{
oleDbDataAdapter1.Fill(ddd1);
abc();
}
In the function abc, we initialize the variables 'cnt' and 'pos' to the total number of rows and
current record position, respectively. The 'pos' variable must be increased by one, since the
index is zero-based, i.e. it starts at zero.
The Text property of the textbox named textBox4, is initialized to the new values. The strings
are placed within double quotes, whereas, the integers are automatically converted to strings,
so that no extra effort is required on our part. The plus sign is employed to join two strings,
and is known as the 'string concatenation operator'.
Screen 6.11
There are numerous records in the dataset. So, we shall now essay towards displaying only
those records that meet certain criteria. To do so, select the OleDbDataAdapter object in the
form, and then, for the property SelectCommand, click on the 'plus' sign, to view the Dynamic
properties.
When the button for the property CommandText having 3 dots is clicked, it displays the screen
as reflected below.
Screen 6.12
Here, we notice the same screen that was displayed in the wizard. The SQL statement is
modified to the following:
SELECT CustomerID, CompanyName, ContactName FROM Customers WHERE City = ?
Addition of the WHERE clause results in the display of customers belonging to a specific city
only. The name of the city will be provided by the user, during program execution. The ? sign is
a parameter or placeholder, which accommodates the name of the city.
Before building and running the application, introduce a textbox and a button.
Change the Text property of the button to 'Show', and then, double click on the button to enter
the following lines of code:
oleDbDataAdapter1.SelectCommand.Parameters["city"].Value = textBox5.Text;
ddd1.Clear();
oleDbDataAdapter1.Fill(ddd1);
abc();
There is just one more assignment to complete, before we wind up. Empty the function
Form1_Load. Now, run the program, and in the newly inserted textbox, enter 'London'. Then,
click on the Show button. The output reveals that the city of London has six customers. Screen
6.13 provides the proof.
Screen 6.13
Now, if you change 'London' to 'Paris', you will see only two customers from this city. Thus,
from amongst the 91 customers, we can selectively view only those who belong to a certain city.
Now let us try to appreciate the 'behind the scene' activities.
The SelectCommand property in the oleDbDataAdapter, has a property named Parameters. It is
actually an indexer that requires a string parameter to reference the parameter 'name'. The
Value property of the indexer is initialized to the city name provided in the Text property of the
textBox5. It is indeed a good idea to clear the dataset of all records, using the clear function.
Then, the same Fill function is called, followed by the abc function. Thus, the presence of
parameters makes our program extremely generic. The above code can be re-written in a
simple format, as shown below.
System.Data.OleDb.OleDbCommand a;
a= oleDbDataAdapter1.SelectCommand;
System.Data.OleDb.OleDbParameterCollection p;
p = a.Parameters;
System.Data.OleDb.OleDbParameter p1;
p1 = p["city"];
p1.Value = textBox5.Text;
ddd1.Clear();
oleDbDataAdapter1.Fill(ddd1);
abc();
The property SelectCommand is of data type OleDbCommand. It belongs to the
System.Data.OleDb namespace. If the 'using' clause is not provided for this namespace, we
have to write the entire name of the namespace. This can be avoided by employing the 'using'
keyword. The OleDbCommand has a property named Parameters, which is of data type
OleDbParameterCollection. This property, in turn, has an indexer that takes the parameter
name as a string parameter, and returns an OleDbParameter. The Value member of this object
p1, is initialized to the Text property of the textbox.
Finally, you can change the Text property of all the textboxes to empty strings, because the
initial display lacks in aesthetic appeal. There are no bounds to the number of enhancements
that we can effect to the above program.
The presence of four copies of the function abc, one for each of the above four click functions,
also does not appeal to us. So, we eliminate the calls to the function abc from the four click
functions, and add the following code to the click function of button5:
BindingManagerBase z;
z= BindingContext[ddd1, "Customers"];
z.PositionChanged += new EventHandler(abc);
Also, the abc function requires to be amended, to read as follows:
void abc( object s, System.EventArgs e)
The BindingManagerBase object z is first initialized, and then, the PositionChanged delegate is
assigned to the function abc. Thus, each time the Position property changes, the abc function
gets called. There is no need for us to call the abc function explicitly, since the system calls it,
whenever there is a change in the Position property.
Every Windows Application comprises of a menu. So, how do we place menus in our Windows
Application? As is generally done, we shall examine this process, one step at a time. We shall
also display the code generated by Visual Studio.Net, so that you are able to grasp the concept
of Menu Handling in C#.
As always, save all the files, and start with a new project, using the File - New - Project menu
option. In the dialog box, select the Project type as Visual C# Project, and in the Template
pane, select the template as Windows Application. Name the project as s2, and locate it at
c:\mukhi. Then, click on OK. You also need to ensure that the Properties and the Toolbox
windows are visible. From the ToolBox, under the Windows From tab, select the MainMenu
control and drag-and-drop it into the Form. We merely witness a message Type Here. This is
shown in screen 6.14. When we insert the word File, the menu changes to the one shown in
screen 6.15.
Screen 6.14 Screen 6.15
On building and running the program, the screen displays the word File, as shown in screen
6.16.
Screen 6.16
On clicking this menu item, nothing gets displayed, and in effect, nothing transpires. We now
go back to the Code Generator, and have a look at the code that is generated.
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
There are two instance variables, mainMenu1 of data type MainMenu, and menuItem1 of data
type MenuItem.
If you click on the word File in the Design Mode, the properties window will display the
properties for this menu item object. This is shown in screen 6.17.
Screen 6.17
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1});
this.menuItem1.Index = 0;
this.menuItem1.Text = "File";
this.Menu = this.mainMenu1;
}
The function InitializeComponent contains the entire menu-generated code. Here, we first
create an instance for the two menu objects. The mainMenu1 object has the MenuItems
property, which in turn, calls the AddRange function. This function adds an array of MenuItem
objects. As we are in possession of only one such object, the array displays this solitary object
named menuItem1. This procedure is similar to the one used earlier for adding controls to the
Form.
The Index property shows 0, since it is the first menu item. The text property displays File. The
Form class has a property called Menu, which is initialized to the object MainMenu1. This
main menu object knows all about the other menu items that are to be displayed.
To add another menu item, click on the word File. This will open up an additional box on the
right. Enter the text 'Edit', as shown in screen 6.18.
Screen 6.18 Screen 6.19
Build and run the application. A screen is displayed, containing two menu items named File
and Edit, as is clearly seen in screen 6.19.
With the introduction of one more menu item, the default name assigned to it becomes
menuItem2. An instance variable of the same name is created in the code file.
private System.Windows.Forms.MenuItem menuItem2;
private void InitializeComponent()
{
this.menuItem2 = new System.Windows.Forms.MenuItem();
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1, this.menuItem2});
this.menuItem2.Index = 1;
this.menuItem2.Text = "Edit";
}
In the InitializeComponent function, additional code gets added for the second menu item. The
object menuItem2 is first initialized and then, it is added to the array, which is to be provided
to the AddRange function. We would have had to add them individually, if the array did not
exist. Thus, adding 10 menu items would have entailed calling the function 10 times.
The index property for this menu item is shown as 1, since it is the second menu item. The text
property shows the newly entered text as Edit.
Now, we shall add an item to the File menu. Click on the word File, and enter the word New, as
displayed in screen 6.20.
Screen 6.20 Screen 6.21
Then, build and run the application. Click on the File menu, and it will display a popup menu,
containing the word New. This is presented in screen 6.21.
Let us now look at the code, which generates the popup menu.
private System.Windows.Forms.MenuItem menuItem3;
private void InitializeComponent()
{
this.menuItem3 = new System.Windows.Forms.MenuItem();
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {this.menuItem1,
this.menuItem2});
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem3});
this.menuItem1.Text = "File";
this.menuItem3.Index = 0;
this.menuItem3.Text = "New";
}
In order to store the word New, one more MenuItem named menuItem3, gets added. A new
instance is also created for this purpose. This menu item is not added to the array provided in
the AddRange function of mainMenu1.MenuItems. However, the menuItem1 object has a
similar property named MenuItems, which has an AddRange function. This function also
accepts an array of MenuItems.
Since only a single MenuItem named New is to be added, this array holds only one member.
The Index property of menuItem3 is assigned a value of zero, since it is the first menu item
under File.
Similarly, if we add one more item named Open just below New, another variable named
menuItem4 will be created, and the Addrange function will now read as follows:
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem3,this.menuItem4});
Thus, there will be two menu item objects in the array, which will be passed to the AddRange
function of the menuItem1 object. Now, each time that the menu item Open is clicked, we
desire that some specific code should be called. Click on the left side of the word Open, with
the left mouse button. You will arrive at the screen shown in 6.22. Here, we see a tick-mark on
the menu.
Screen 6.24
Save all the files and create a new project. In the dialog box, select the project type as Visual
C# Project, and the Template as a Windows Application. Name the project as s3, and locate it
at c:\mukhi. Then, click on OK. As before, the Properties Window and the Toolbox must be
activated. Move down the scroll bar in the toolbox, and select the control ContextMenu. Drag-
and-drop it into the form.
Screen 6.25 shows the screen that encompasses the introduction of the ContextMenu.
Screen 6.25
The generated code contains the instance variable contextMenu1, of data type ContextMenu.
private System.Windows.Forms.ContextMenu contextMenu1;
private void InitializeComponent ()
{
this.contextMenu1 = new System.Windows.Forms.ContextMenu();
}
Also, in the function InitializeComponent, the instance variable contextMenu1 is initialized to a
ContextMenu object. No additional code is required, since a ContextMenu is always added to a
control. Clicking with the right mouse button on the control, displays this menu. Hence, it is
termed as a ContextMenu.
Now, we shall add two menu items to this Context Menu. Make sure that the Context Menu
object is highlighted at the bottom of the screen. Then, click on the words ContextMenu, as
exhibited in screen 6.26.
Screen 6.26
Replace the words 'Type Here' with the word 'New'. Then, click on the words 'Type Here' given
below, and replace it with 'Open'.
Thus, we now obtain two menu items in the context menu. But, the moment we click on the
Form, the context menu disappears, since it is required to be displayed only when the context
menu control is selected. This is unlike a main menu object. The extra code that is required for
adding the items to a context menu, is as follows:
private System.Windows.Forms.MenuItem menuItem1;
private System.Windows.Forms.MenuItem menuItem2;
private void InitializeComponent ()
{
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.menuItem2 = new System.Windows.Forms.MenuItem();
this.contextMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1,this.menuItem2});
this.menuItem1.Index = 0;
this.menuItem1.Text = "New";
this.menuItem2.Index = 1;
this.menuItem2.Text = "Open";
}
For the two menu items New and Open, there exist two instance variables, menuItem1 and
menuItem2. In the function InitializeComponent, the two menu item objects are initialized;
then, the AddRange function is used, which is present in the MenuItems collection property of
the context menu object contextMenu1. The array passed to this function contains the two
menu items as its members. Thus, a context menu is no different from any other menu.
Let us return back to the Design Mode and incorporate a button and a textbox in the Form.
Select the button, and in the Properties Window, move to the property called ContextMenu.
Click on the drop down listbox to arrive at a list of context menus, which are currently
available for the Form. Since only one contextmenu1 is present, a solitary option is revealed.
Select this option. Then, choose the textbox, and in the drop down listbox of the property
ContextMenu, pick the context menu named contextMenu1.
On having done so, nothing earth-shattering occurs. If you cast a glance at the code, you will
notice that the property ContextMenu of the Textbox, and that of the Button, has been
initialized to contextmenu1. Press F5 to run the program, and then, right click with the mouse
on the Button. And there you go! The menu is now visible on the screen, containing two items,
viz. New and Open. This is shown in screen 6.27.
Screen 6.27
We now incorporate a few amendments to the application, by adding a few more menu items to
the context menu. We also activate a function, when the control associated with the context
menu is clicked.
To do so, add the following line of code to the InitializeComponent function:
this.contextMenu1.Popup += new System.EventHandler(abc);
The ContextMenu class has an event called Popup, which is associated with the function abc.
This event gets triggered, whenever we click on the right mouse button. It is a good practice to
place similar code together. This is merely a suggestion. Accordingly, we have placed all code
related to the ContextMenu together.
We have placed the above code immediately after the AddRange function. The exact location
does not matter, as long as it is placed inside the function, and is located after an instance has
been created.
We also need to create the function abc, and call the Show function in it. The code specified
below is placed after the Main function.
private void abc(object sender, System.EventArgs e)
{
MessageBox.Show("hi");
}
Press F5 to run the program, and then, click with the right mouse button. Before the popup
springs up, we notice a Message Box, as shown in screen 6.28.
Screen 6.34
Now, usher-in another MainMenu object on the form, and for the first menu item, assign the
text "Sonal", and for the one below it, enter "File". The screen 6.35 displays the outcome of
these actions.
Screen 6.35
We may acquire as many MainMenu objects as desired, but only one of them will be active at
any given time. So, the form will initially show the first mainMenu1 to be active. The second
menu object mainMenu2 will become visible, only when it is clicked on, in the bottom frame.
Now introduce two buttons into the Form.
Change the Text property of the first button to 'First', and then, double click on the button. In
the function button1_Click, enter the following code:
Menu = mainMenu1;
Similarly, change the Text property of the second button to 'Second', and double click on the
button, to enter the following line of code:
Menu = mainMenu2;
Once this is achieved, build and run the program. The first menu called "vijay", will be visible
by default. Clicking on the button labeled "Second", will immediately result in a switch to
"Sonal". This is shown in screen 6.36.
Screen 6.36
Clicking on the button labeled "First", will again revert the menu back to the original, i.e. to
"Vijay".
Thus, we are empowered to change the main menu at any given time. We are also permitted to
have as many menu objects as we like. Further, we can point the Menu property to any Menu
object that we desire. Thus, at startup, a user sees a solitary menu, and after accomplishing
certain tasks, the menu switches to a more complex layout.
Thus, you may have realized by now, that it is much more productive to work with a Menu
Painter, than to manually write lines of tedious code.