5

 

Data Handling

 

a.cs

using System;

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

using System.Data;

using System.Data.OleDb;

using System.IO;

using System.Collections;

public class CustomerList : System.Collections.CollectionBase

{

public static CustomerList GetCustomers()

{

CustomerList cl = new CustomerList();

Customer a = Customer.ReadCustomer1();

IList b = cl.List;

b.Add(a);

Customer c = new Customer("246-12-5645");

c.FirstName = "Vijay";

c.DateOfBirth = DateTime.Parse("5/3/1933");

cl.Add1(c);

cl.Add1(Customer.ReadCustomer2());

return cl;

}

public int Add1(Customer value)

{

return List.Add(value);

}

}

public class Customer : Component

{

public string id,FirstName ;

public DateTime dateOfBirth;

public static Customer ReadCustomer1()

{

Customer cust = new Customer("536-45-1245");

cust.FirstName = "Sonal";

cust.DateOfBirth = DateTime.Parse("9/9/1941");

return cust;

}

public static Customer ReadCustomer2()

{

Customer cust = new Customer("651-27-8117");

cust.FirstName = "Manish";

cust.DateOfBirth = DateTime.Parse("3/25/1942");

return cust;

}

public Customer(string ID): base()

{

id = ID ;

}

public string ID

{

get 

{

return id ;

}

}

public string FirstName1

{

get 

{

return FirstName ;

}

}

public DateTime DateOfBirth

{

get 

{

return dateOfBirth ;

}

set

{

dateOfBirth = value ;

}

}

}

public class zzz : Form

{

TextBox textBoxPosition;

CustomerList custList;

void textBoxDOB_FormatDate(object sender, ConvertEventArgs e)

{

if (e.DesiredType != typeof(string))

return ;

if (e.Value.GetType() != typeof(DateTime))

return ;

DateTime dt = (DateTime)e.Value;

e.Value = dt.ToLongDateString();

}

void textBoxDOB_ParseDate(object sender, ConvertEventArgs e)

{

if (e.DesiredType != typeof(DateTime))

return ;

if (e.Value.GetType() != typeof(string))

return ;

string value = (string)e.Value;

try

{

e.Value = DateTime.Parse(value);

}

catch(Exception ex)

{

MessageBox.Show(ex.Message);

}

}

void buttonMoveFirst_Click(object sender, System.EventArgs e)

{

BindingContext[custList].Position = 0 ;

}

void buttonMoveLast_Click(object sender, System.EventArgs e)

{

BindingContext[custList].Position = custList.Count - 1;

}

void buttonMoveNext_Click(object sender, System.EventArgs e)

{

if (BindingContext[custList].Position < custList.Count - 1)

{

BindingContext[custList].Position++;

}

}

void buttonMovePrev_Click(object sender, System.EventArgs e)

{

if (BindingContext[custList].Position > 0)

{

BindingContext[custList].Position--;

}

}

void customers_PositionChanged(object sender, System.EventArgs e)

{

textBoxPosition.Text = "Record " + (BindingContext[custList].Position + 1) + " of " + custList.Count;

}

public zzz() {

Text = "Customer Details";

ClientSize = new System.Drawing.Size(368, 413);

MinimumSize = new Size(368, (413 + SystemInformation.CaptionHeight));

Label labelFirstName,labelID,labelDOB;

labelID = new Label();

labelID.Location = new System.Drawing.Point(8, 32);

labelID.Text = "ID:";

labelID.Size = new System.Drawing.Size(64, 16);

labelFirstName = new Label();

labelFirstName.Location = new System.Drawing.Point(8, 112);

labelFirstName.Text = "&First Name:";

labelFirstName.Size = new System.Drawing.Size(64, 16);

labelDOB = new Label();

labelDOB.Location = new System.Drawing.Point(8, 194);

labelDOB.Text = "&Date of Birth:";

labelDOB.Size = new System.Drawing.Size(92, 16);

TextBox textBoxDOB,textBoxFirstName,textBoxID;

textBoxID = new TextBox();

textBoxID.Location = new System.Drawing.Point(88, 30);

textBoxID.ReadOnly = true;

textBoxID.Enabled = false;

textBoxID.Size = new System.Drawing.Size(203, 20);

textBoxFirstName = new TextBox();

textBoxFirstName.Location = new System.Drawing.Point(88, 112);

textBoxFirstName.Size = new System.Drawing.Size(243, 20);

textBoxDOB = new TextBox();

textBoxDOB.Location = new System.Drawing.Point(88, 192);

textBoxDOB.Size = new System.Drawing.Size(243, 20);

textBoxPosition = new TextBox();

textBoxPosition.Location = new System.Drawing.Point(88, 14);

textBoxPosition.ReadOnly = true;

textBoxPosition.Enabled = false;

textBoxPosition.Size = new System.Drawing.Size(88, 20);

Button buttonMoveFirst,buttonMovePrev,

buttonMoveNext,buttonMoveLast;

buttonMoveNext = new Button();

buttonMoveNext.Location = new System.Drawing.Point(184, 8);

buttonMoveNext.FlatStyle = FlatStyle.Flat;

buttonMoveNext.Size = new System.Drawing.Size(32, 32);

buttonMoveNext.Text = ">";

buttonMoveNext.Click += new System.EventHandler(buttonMoveNext_Click);

buttonMovePrev = new Button();

buttonMovePrev.Location = new System.Drawing.Point(48, 8);

buttonMovePrev.FlatStyle = FlatStyle.Flat;

buttonMovePrev.Size = new System.Drawing.Size(32, 32);

buttonMovePrev.Text = "<";

buttonMovePrev.Click += new System.EventHandler(buttonMovePrev_Click);

buttonMoveFirst = new Button();

buttonMoveFirst.Location = new System.Drawing.Point(8, 8);

buttonMoveFirst.FlatStyle = FlatStyle.Flat;

buttonMoveFirst.Size = new System.Drawing.Size(32, 32);

buttonMoveFirst.Text = "|<";

buttonMoveFirst.Click += new System.EventHandler(buttonMoveFirst_Click);

buttonMoveLast = new Button();

buttonMoveLast.Location = new System.Drawing.Point(224, 8);

buttonMoveLast.FlatStyle = FlatStyle.Flat;

buttonMoveLast.Size = new System.Drawing.Size(32, 32);

buttonMoveLast.Text = ">|";

buttonMoveLast.Click += new System.EventHandler(buttonMoveLast_Click);

Panel panelVCRControl;

panelVCRControl = new Panel();

panelVCRControl.Location = new System.Drawing.Point(88, 344);

panelVCRControl.Size = new System.Drawing.Size(264, 48);

panelVCRControl.Text = "panel1";

panelVCRControl.Controls.AddRange(new Control[]

{textBoxPosition,buttonMoveFirst,buttonMovePrev,

buttonMoveNext,buttonMoveLast});

Controls.AddRange(new Control[]

{textBoxDOB,labelDOB,panelVCRControl,textBoxFirstName,

textBoxID,labelFirstName,labelID});

custList = CustomerList.GetCustomers();

ControlBindingsCollection a = textBoxID.DataBindings;

a.Add("Text", custList, "ID");

textBoxFirstName.DataBindings.Add("Text", custList, "FirstName1");

Binding dobBinding = new Binding("Text", custList, "DateOfBirth");

dobBinding.Format += new ConvertEventHandler(textBoxDOB_FormatDate) ;

dobBinding.Parse += new ConvertEventHandler(textBoxDOB_ParseDate) ;

textBoxDOB.DataBindings.Add(dobBinding);

BindingManagerBase c = BindingContext[custList];

c.PositionChanged += new EventHandler(customers_PositionChanged);

textBoxPosition.Text = "Record " + (BindingContext[custList].Position + 1) + " of " + custList.Count;

}

public static void Main() {

Application.Run(new zzz());

}

}

 

Before submerging deep into the topic of Data Binding with Windows Forms Controls, let us first address the issues of the User Interface.

 

Screen 5.1

 

In the zzz constructor, the Text property of the Form class, which is initialized to 'Customer Details', dons the mantle of the window title. The ClientSize property determines the size of the Form. The MinimumSize property ensures that the size is not reduced beyond the value specified. We have dealt with these properties quite a while ago. The User Interface code is more germane for creating a visually appealing window, than for ameliorating our understanding of the core concepts of Data Binding.

 

We intend to display three text labels in our window. To facilitate this, it is essential to create three label controls viz., labelFirstName, labelID and labelDOB. The text property of each is initialized to ID, First Name and Date of Birth respectively. We have deliberately not altered the names of the controls provided in the original sample.  Furthermore, we have not modified any of the label control codes, in order to facilitate effortless comprehension. Besides Text, the other properties of the label control that we modify are the Location and the Size.

 

This user interface code is usually written by a utility called the Screen Painter and not by the programmer. This is because, it is very irksome and arduous for a programmer to supply coordinates, in pixels, for Properties such as the Location etc.

 

We will display data employing the services of a textbox. Thus, three textboxes named textBoxDOB, textBoxFirstName and textBoxID, have been provided, to store the date of birth, first name of customer and the customer ID, respectively. The Size of each textbox is defined by modifying the value contained in the Size property. Thereafter, a new location is specified using the Location property. The textbox displaying the customer ID has the ReadOnly property set to true, so that its contents cannot be altered.  Moreover, setting the Disabled property to false disables the field. One more textbox control called textBoxPosition is introduced. It displays the current active record and the total number of records in the recordset. We shall not discuss the textbox properties any further in the forthcoming programs.

 

We now need buttons in our window to enable the user to navigate between records. To attain this, the four button controls and their corresponding actions are given below:

     buttonMovePrev          : Moves to the previous record.

     buttonMoveNext          : Moves to the next record.

     buttonMoveFirst         : Jumps to the first record.

     buttonMoveLast          : Jumps to the last record.

 

We can set the properties of Location, Size and Text of the button controls to suit our requirements. For an enhanced visual appeal, the FlatStyle property is also altered. We use the Click event to wire up each button to a corresponding method having a similar name. This is done to ensure that every time we click on a button, the desired code gets executed. The code that gets activated will be dealt with subsequently.

 

A Panel control, by itself, is worthless, since it does nothing. Its role becomes consequential only when it aggregates or collects other controls. By placing controls within a Panel control, we can deal with all of them simultaneously. Thus, with a single line of code, we can disable a panel, thereby effectively, disabling all the controls contained therein. So, by using a panel control, a large number of controls can be treated as a single control and all their properties can be changed in unison. The Location and Size properties of the Panel control named panelVCRControl are set to certain specific co-ordinates. The Text property is initialized, but it does not get displayed on the screen.

 

The Panel control, like any other control, has a Controls property having a data type of Control.ControlCollection. Using the AddRange method that requires an array, all controls are added in a single action to the ControlCollection. This is analogous in functionality to the Add function, which adds only a single control to the Controls Collection. Thus, internally, AddRange repetitively calls the Add function, and in each iteration, supplies it with a single member of the array, till it has passed all the members of the entire array as parameters.

 

To summarize, we add the four textboxes and one label control to the panel, so that we can treat them as a single entity. In this program, we however, are not utilizing this property. The rest of the controls, including the panel control, are finally added to the main Form using the above AddRange function.

 

The object custList is of user-defined data type CustomerList, which is derived from CollectionBase. This class contains a static function called GetCustomers. This function creates an object c1, which is an instance of CustomerList. By using New and Finally, the value in this object is returned to custList in the zzz constructor.

 

We have another class Customer, which is derived from the Component class (this is optional). It represents a single Customer. The CustomerList class symbolizes a list or an assemblage of customers. The Customer class has a static function ReadCustomer1 that creates an instance of class Customer and passes the customer ID to the constructor. The constructor initializes the field id with this value.

 

The Customer object is represented by three variables, viz., id, FirstName and dateofBirth. Thus, an object is identified by its fields or variables, and not by the methods it employs. The programmers at Microsoft chose to initialize the id field through the constructor and the other fields separately. They could instead have initialized all three fields through the constructor or initialized none at all.

 

The most noteworthy thing here is that the FirstName field can be directly accessed, whereas, the dateofBirth field is accessible only through the property DateofBirth, using its set accessor. It is sensible and prudent to prevent access to a field directly, and allow access only through a property. The id field, however, is accessed through the constructor and not through the property id. We shall delve upon this, before long.

 

Object a represents the first customer. This object is stored in the CustomerList class since it is derived from class CollectionBase. It has the ability to store multiple objects. The CollectionBase class has a property called List of data type lList, which represents the collection. An IList object named b has a method Add, which adds any object to the Collection and returns the position where the object has been added. Thus, we have added a Customer object to the list.

 

To add the second customer, we create a Customer object in the same class, and initialize the members directly. This customer is also added to the CustomerList by calling a function Add1, which uses the List property to Add the customer.

 

The third customer is added to the List, using a more compact form. Each one of us possesses a distinct style of writing code. Thus, it is ineffectual to debate over the issue of why a static function has been used by us to create an object, instead of creating it directly.

 

The CustomerList class, which is derived from Collections, can store any arbitrary object. The Add1 function is not essential, but it facilitates the addition of objects to the collection. Thus, the CustomerList class is a simple collection of objects. Any other entity that can represent a collection could also have been used instead.

 

Reverting back to our constructor zzz, the Custlist object now provides access to the three customer objects through a collection object. Every control has a ReadOnly property called DataBindings, which is of the data type ControlBindingsCollection. ControlBindingsCollection in turn, is derived from class BindingsCollection. This class is used to bind a control to the data source, since it represents a collection of all data bindings for a control.

 

 

The Add function accepts three parameters and returns a Binding object.

     The first parameter is a string representing the name of the property of the control that we want to bind to. In our program, we have used the property named Text.

     The second parameter is of type object, which represents the data i.e. the collection object or data source. In this case, it is custlist.

     The third parameter is the name of the field or property that we need to bind to. Here, the field name is id.

 

Thus, we are binding a field called id in the data source custList to the first textbox's Text property. Using the above mechanism, we could bind any column in the data source to any valid control property, such as, backcolor or forecolor.

 

In the next set, we bind the Text property of the second textbox control, textBoxFirstName, to the column FirstName1 in the data source custlist.

 

The third column is added with the help of a class called Binding, which only understands Binding. This class represents a simple relationship between the property of a control and that of any object. The constructor of the Binding class is given the same three parameters as those of the Add function, and they also have the same significance.

 

We shall now explain the second parameter, i.e. a data source. The second parameter could be any class that derives from interfaces, IBindingList or ITypedList. These comprise of the DataSet, DataTable, DataView, or DataViewManager classes. These classes implement the IList interface. In effect, there are a large number of classes (over 20), which implement from this interface.  We have used CollectionBase in our present example. The only safeguard to be kept in mind is that, an IList object has to be created first, and only then can it be used in any of the bindings functions. The object in the list must be of the same data type, or else, an exception will get thrown. The last type permitted as a data source is a strongly typed IList such as an array.

 

One commendable feature about the Binding class is that it permits the user to determine the display patterns of data. It also acts as the validating authority, whenever the user makes any alteration to the data. The Binding manager calls the Format event when it has to display some data in the control and calls the Parse event when it has to retrieve data. Thus, we can build our own custom formats.

 

The Format event is attached to a function called textBoxDOB_FormatDate, using the delegate ConvertEventHandler. This function supplies the custom format in which the date is to be displayed. We use the same principles to call the method textBoxDOB_ParseDate, which parses the date and checks for errors. We shall make an endeavor to grasp these functions, in a short while.

 

The Add function used earlier, was overloaded to accept either of the following:

     two strings and an object parameter.

     two strings and a Binding object.

 

Thus, the only difference between the two data bindings is that, by using a Binding object, we are able to customize the display of data.

 

The Form class has a BindingContext property that returns a BindingContext object. The indexer returns a BindingManagerBase object, which represents all data-bound controls, which are bound to the same data source, and keeps them synchronized. It is this BindingManagerBase object that facilitates movement from one record to another. As of now, we initialize its PositionChanged event to a function customers_PositionChanged, which will be called each time the Position property changes. The secret of how this is done shall be revealed at a later date.

 

Any class derived from Collections has a member called Count, which returns the number of objects present in the Collection. In our case, the count is shown as 3. The BindingManagerBase has a member called Position that exposes the object (which is the current object in the list or data source), to which the control is bound. This index is zero based i.e. the first object in the list is numbered zero. And since it is zero based, the Text property of the label in the panel is initialized to a string containing the value of the Position property + 1. It is followed by the string 'of', and finally by the value returned by the Count property of the custList object. The text is finally displayed as '1 of 3'.

 

Screen 5.2

 

When the form loads on, the first object is the active object. The BindingManagerBase class now ensures that the Text property of the three textboxes is initialized to the appropriate value.

 

The first textbox is bound to a field called ID. Thus, the Customer class is searched for a property called ID. The 'get' accessor is called. The value returned by it is the value displayed in the textbox. Thus, the Customer class needs a property called ID with a 'get' accessor. If the name of the property is modified, say to ID1, or if the get accessor is removed, an exception is generated at run time; however, no errors will be generated at compile time.

 

The same holds true for the FisrtName1 property and the DateofBirth. The DateofBirth differs, in that, the function textBoxDOB_FormatDate is called after the get accessor is called. This is done to facilitate display of the date in the format approved by us. The second parameter 'e' in textBoxDOB_FormatDate, which is of data type ConvertEventArgs, is well acquainted with the object that is to be displayed in the textbox. The DesiredType property of the parameter class contains the original data type of the property that is bound in the data source. If its type is not DateTime, the program exits from the function gracefully using Return. This parameter 'e' has a property called Value, which contains the actual unformatted value that exists in the data source. As the return value of the property is object, we use the GetType function to retrieve the type of this unformatted value. If the type is a string, the program continues execution, or else, it exits. Yet another error check!

 

The fact that the program has been able to pass beyond the above two error checks successfully establishes that the value in hand can now be formatted to the type we desire. Therefore, we first cast this value into a DateTime object dt, and then, use the ToLongDateString function from the class, to convert the date into a string using the long form. This value is stored back into the Value property of the parameter 'e' and is displayed as the Text property of the textbox.

 

Whenever we attempt at altering the date into a valid or an invalid one, the function textBoxDOB_ParseDate or the Parse event gets called. This function runs the same two error checks on the date, and thereafter, stores the value of the Value property in a string. Thereafter, the string is converted into a datetime object. If this process does not score a success for any reason, an exception is thrown and a MesssageBox is displayed. This modified value in the Value property of the parameter, is stored back in the data source by the framework.

 

Screen 5.3

 

When the form loads on, we notice the first record from the data source. To see the next record, we have to press the button with the display of the > arrow. This is the buttonMoveNext control. This action consecutively, calls function buttonMoveNext_Click that uses the BindingManagerBase object and the BindingContext to increment the Position property by a value of 1. This process is encapsulated in an 'if' statement, so that a check can be performed on whether the record is the last one in the list or not. The last record is retrieved, using the Count property of the data source.

 

The subtraction of 1 is mandatory, since the Position property is zero based. To move backwards, the Position property is decremented by 1, and the 'if' statement verifies whether the value is greater than zero or not. To move to the first record, we set the Position property to 0, and to move to the last record, we set the Position property to Count-1. Each time we change the Position property, the function customers_PositionChanged gets called. Here, the textbox was updated in a manner similar to what was done earlier to change the position of the record pointer.

 

The program is considerably extensive, but it exhibits the data binding properties of a control, in order to display objects from a data source.

 

a.cs

using System;

using System.ComponentModel;

using System.Drawing;

using System.Windows.Forms;

using System.Data;

using System.IO;

using System.Collections;

public class zzz : Form {

DataTable t;

int cnt;

TextBox textBoxPosition;

void buttonMoveFirst_Click(object sender, System.EventArgs e)

{

BindingContext[t].Position = 0 ;

}

void buttonMoveLast_Click(object sender, System.EventArgs e)

{

BindingContext[t].Position = cnt - 1;

}

void buttonMoveNext_Click(object sender, System.EventArgs e)

{

if (BindingContext[t].Position < cnt - 1)  {

BindingContext[t].Position++;

}

}

void buttonMovePrev_Click(object sender, System.EventArgs e)

{

if (BindingContext[t].Position > 0)

{

BindingContext[t].Position--;

}

}

void customers_PositionChanged(object sender, System.EventArgs e)

{

textBoxPosition.Text = "Record " + (BindingContext[t].Position + 1) + " of " + cnt;

}

public zzz() {

Text = "Customer Details";

ClientSize = new System.Drawing.Size(368, 413);

MinimumSize = new Size(368, (413 + SystemInformation.CaptionHeight));

TextBox textBoxFirstName;

textBoxFirstName = new TextBox();

textBoxFirstName.Location = new System.Drawing.Point(88, 112);

textBoxFirstName.Size = new System.Drawing.Size(243, 20);

textBoxPosition = new TextBox();

textBoxPosition.Location = new System.Drawing.Point(88, 14);

textBoxPosition.ReadOnly = true;

textBoxPosition.Enabled = false;

textBoxPosition.Size = new System.Drawing.Size(88, 20);

Button buttonMoveFirst,buttonMovePrev,buttonMoveNext,buttonMoveLast;

buttonMoveNext = new Button();

buttonMoveNext.Location = new System.Drawing.Point(184, 8);

buttonMoveNext.FlatStyle = FlatStyle.Flat;

buttonMoveNext.Size = new System.Drawing.Size(32, 32);

buttonMoveNext.Text = ">";

buttonMoveNext.Click += new System.EventHandler(buttonMoveNext_Click);

buttonMovePrev = new Button();

buttonMovePrev.Location = new System.Drawing.Point(48, 8);

buttonMovePrev.FlatStyle = FlatStyle.Flat;

buttonMovePrev.Size = new System.Drawing.Size(32, 32);

buttonMovePrev.Text = "<";

buttonMovePrev.Click += new System.EventHandler(buttonMovePrev_Click);

buttonMoveFirst = new Button();

buttonMoveFirst.Location = new System.Drawing.Point(8, 8);

buttonMoveFirst.FlatStyle = FlatStyle.Flat;

buttonMoveFirst.Size = new System.Drawing.Size(32, 32);

buttonMoveFirst.Text = "|<";

buttonMoveFirst.Click += new System.EventHandler(buttonMoveFirst_Click);

buttonMoveLast = new Button();

buttonMoveLast.Location = new System.Drawing.Point(224, 8);

buttonMoveLast.FlatStyle = FlatStyle.Flat;

buttonMoveLast.Size = new System.Drawing.Size(32, 32);

buttonMoveLast.Text = ">|";

buttonMoveLast.Click += new System.EventHandler(buttonMoveLast_Click);

Panel panelVCRControl;

panelVCRControl = new Panel();

panelVCRControl.Location = new System.Drawing.Point(88, 344);

panelVCRControl.Size = new System.Drawing.Size(264, 48);

panelVCRControl.Text = "panel1";

panelVCRControl.Controls.AddRange(new Control[]

{textBoxPosition,buttonMoveFirst,buttonMovePrev,

buttonMoveNext,buttonMoveLast});

Controls.AddRange(new Control[]

{panelVCRControl,textBoxFirstName});

t = MakeTable();

textBoxFirstName.DataBindings.Add("Text", t, "Text");

textBoxFirstName.DataBindings.Add("BackColor", t, "BackColor");

textBoxFirstName.DataBindings.Add("ForeColor", t, "ForeColor");

BindingManagerBase c = BindingContext[t];

c.PositionChanged += new EventHandler(customers_PositionChanged);

cnt = t.Rows.Count;

textBoxPosition.Text = "Record " + (BindingContext[t].Position + 1) + " of " + cnt;

}

private DataTable MakeTable() {

DataTable t = new DataTable("Control");

t.Columns.Add("BackColor", typeof(Color));

t.Columns.Add("ForeColor", typeof(Color));

t.Columns.Add("Text");

DataRow r;

r = t.NewRow();

r["BackColor"] = Color.Blue;

r["ForeColor"] = Color.Yellow;

r["Text"] = "Yellow on Blue";

t.Rows.Add(r);

r = t.NewRow();

r["BackColor"] = Color.White;

r["ForeColor"] = Color.Green;

r["Text"] = "Green on white";

t.Rows.Add(r);

r = t.NewRow();

r["BackColor"] = Color.Orange;

r["ForeColor"] = Color.Black;

r["Text"] = "Black on Orange";

t.Rows.Add(r);

return t;

}

public static void Main() {

Application.Run(new zzz());

}

}

 

Screen 5.4

 

This example borrows sufficiently from the previous example. The user interface too is almost similar to the earlier one.

 

This program has one textbox called textBoxFirstName, which has 3 data bindings on properties of Text, BackColor and ForeColor. The data source is a DataTable object t. The properties in the DataTable have the same name as the bound textbox properties.

 

To create a DataTable object, we create a new instance of DataTable and pass a string, Control, that signifies the name of the table. Every data table desires columns. Hence, there is a Columns property that is a collection. The Columns collection has an Add method that adds a column. The Column names specified must correspond with those given in the Bindings.

 

The first two columns are of data type Color, and the last column called Text, is of the data type String. After having added 3 columns, we need to add a row. The NewRow function returns a blank DataRow object. So, we use the indexer of the data row and pass the column name as a parameter to the indexer, in order to initialize the columns. Finally, we use the Add function of the Rows Collection to add a new row. This process is repeated thrice, once for each column.

 

Screen 5.5

 

The difference here is that, the data source has changed from a Collection to a DataTable object. The column names correspond to the property names specified in the Bindings function. Thus, they can now replace the property names. We can bind multiple properties of a control to fields from a data source. The Binding manager handles this internally. Thus, each time we move from one row to another in the DataTable, three properties of the textbox get modified, and we get to see a colorful output.

 

The Rows Collection has a member called Count, which signifies the number of records or rows present in the DataTable.

 

So far, we have discovered that one or more properties of a control can be bound or associated with a field, column or property of a data source. Thereafter, the Binding Context is employed to move the record pointer in the data source. The framework then ensures that the properties of the control get updated automatically.

 

We can bind as many properties of a control as we desire, and the data source too can be of different types, with strings attached. If it is a DataTable, we need to bind to column names, however, if it is a Collections object, we require a property with a get accessor.

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Globalization;

using System.Windows.Forms;

public class zzz : Form {

Button button1,button2,button3,button4;

TextBox text1,text2,text3,text4;

BindingManagerBase bmCustomers,bmOrders;

DataSet ds;

DateTimePicker DateTimePicker1;

void DecimalToCurrencyString(object sender, ConvertEventArgs cevent)

{

if(cevent.DesiredType != typeof(string)) return;

cevent.Value = ((decimal) cevent.Value).ToString("c");

}

void CurrencyStringToDecimal(object sender, ConvertEventArgs cevent)

{

if(cevent.DesiredType != typeof(decimal)) return;

cevent.Value = Decimal.Parse(cevent.Value.ToString(),NumberStyles.Currency, null);

}

protected void button1_Click(object sender, System.EventArgs e)

{

bmCustomers.Position -= 1;

}

protected void button2_Click(object sender, System.EventArgs e)

{

bmCustomers.Position += 1;

}

protected void button3_Click(object sender, System.EventArgs e)

{

bmOrders.Position-=1;

}

protected void button4_Click(object sender, System.EventArgs e)

{

bmOrders.Position+=1;

}

protected void BindControls()

{

text1.DataBindings.Add(new Binding("Text", ds, "customers.custName"));

text2.DataBindings.Add(new Binding("Text", ds, "customers.custID"));

DateTimePicker1.DataBindings.Add(new Binding("Value", ds, "customers.CustToOrders.OrderDate"));

Binding b = new Binding("Text", ds, "customers.custToOrders.OrderAmount");

b.Parse+=new ConvertEventHandler(CurrencyStringToDecimal);

b.Format+=new ConvertEventHandler(DecimalToCurrencyString);

text3.DataBindings.Add(b);

text4.DataBindings.Add(new Binding("Text", ds, "customers.CustToOrders.custID"));

bmCustomers = BindingContext [ds, "Customers"];

bmOrders = BindingContext[ds, "customers.CustToOrders"];

}

void MakeDataSet()

{

ds = new DataSet("myDataSet");

DataTable tCust = new DataTable("Customers");

DataTable tOrders = new DataTable("Orders");

DataColumn cCustID = new DataColumn("CustID");

DataColumn cCustName = new DataColumn("CustName");

tCust.Columns.Add(cCustID);

tCust.Columns.Add(cCustName);

DataColumn cID = new DataColumn("CustID");

DataColumn cOrderDate = new DataColumn("orderDate",typeof(DateTime));

DataColumn cOrderAmount = new DataColumn("OrderAmount", typeof(decimal));

tOrders.Columns.Add(cOrderAmount);

tOrders.Columns.Add(cID);

tOrders.Columns.Add(cOrderDate);

ds.Tables.Add(tCust);

ds.Tables.Add(tOrders);

DataRelation dr = new DataRelation("custToOrders", cCustID , cID);

ds.Relations.Add(dr);

DataRow newRow1,newRow2;

for(int i = 1; i < 4; i++)

{

newRow1 = tCust.NewRow();

newRow1["custID"] = "Cust " + i;

tCust.Rows.Add(newRow1);

}

tCust.Rows[0]["custName"] = "Vijay";

tCust.Rows[1]["custName"] = "Sonal";

tCust.Rows[2]["custName"] = "Manish";

for(int i = 1; i < 4; i++)

{

for(int j = 1; j < 6; j++)

{

newRow2 = tOrders.NewRow();

newRow2["CustID"]=  "Cust " + i;

newRow2["orderDate"]= new DateTime(2001, i, j * 2);

newRow2["OrderAmount"] = i * 10 + j  * .1;

tOrders.Rows.Add(newRow2);

}

}

}

public zzz() {

Text = "Binding Sample";

ClientSize = new System.Drawing.Size(450, 200);

button1 = new Button();

button1.Location = new System.Drawing.Point(24, 16);

button1.Size = new System.Drawing.Size(64, 24);

button1.Text = "<";

button1.Click+=new System.EventHandler(button1_Click);

button2 = new Button();

button2.Location = new System.Drawing.Point(90, 16);

button2.Size = new System.Drawing.Size(64, 24);

button2.Text = ">";

button2.Click+=new System.EventHandler(button2_Click);

button3 = new Button();

button3.Location = new System.Drawing.Point(90, 100);

button3.Size = new System.Drawing.Size(64, 24);

button3.Text = "<";

button3.Click+=new System.EventHandler(button3_Click);

button4 = new Button();

button4.Location = new System.Drawing.Point(150, 100);

button4.Size = new System.Drawing.Size(64, 24);

button4.Text = ">";

button4.Click+=new System.EventHandler(button4_Click);

text1= new TextBox();

text1.Location = new System.Drawing.Point(24, 50);

text1.Size = new System.Drawing.Size(150, 24);

text2= new TextBox();

text2.Location = new System.Drawing.Point(190, 50);

text2.Size = new System.Drawing.Size(150, 24);

text3= new TextBox();

text3.Location = new System.Drawing.Point(290, 150);

text3.Size = new System.Drawing.Size(150, 24);

text4= new TextBox();

text4.Location = new System.Drawing.Point(9, 150);

text4.Size = new System.Drawing.Size(70, 24);

DateTimePicker1 = new DateTimePicker();

DateTimePicker1.Location = new System.Drawing.Point(90, 150);

DateTimePicker1.Size = new System.Drawing.Size(200, 800);

Controls.Add(button1);

Controls.Add(button2);

Controls.Add(button3);

Controls.Add(button4);

Controls.Add(text1);

Controls.Add(text2);

Controls.Add(text3);

Controls.Add(text4);

Controls.Add(DateTimePicker1);

MakeDataSet();

BindControls();

}

public static void Main()

{

Application.Run(new zzz());

}

}

The above example utilizes more controls as compared to the earlier ones. It also exploits a more complicated data source.

 

Screen 5.6

 

On the screen, we see 4 buttons in sets of two, which assist the record pointer in navigating within the data source.  We also have three textboxes and a control named DateTimePicker control, which activates a calendar. We have already enlightened you on the subject of this control, in the previous chapter. We add these controls using the Add function. The function MakeDataSet is then employed to create a complicated relationship between the data.

 

Let us take a steal peek into the function MakeDataSet.

 

In this function, we create a DataSet object and then pass a string called myDataSet to the constructor. This string is used to provide a name to the root document element in the XML representation. Since there is little utility in assigning a name to the data in this program currently, it can be edged out and abandoned. A DataSet is a collection of tables in the memory, which can be related to each other. We can write volumes on the DataSet concept. Microsoft has pulled out all stops in integrating a large number of features in a DataSet.

 

The next task in hand is to associate two DataTables with our DataSet object. Whenever we require more than one of a similar kind, we need to assign a name to that entity. So, we create two tables tCust and tOrders with Customers and Orders.

We create two DataColumn objects named cCustID and cCustName, and add them to the Columns Collection of our DataTable Customer, using the Add member. The two columns represent the unique ID and the customer name.

 

We then add the following three columns to our table called Orders:

     CustID: the id of the customer who bought the order.

     orderDate: the date on which the order was placed. 

     OrderAmount: the amount of the order placed.

 

Once this has been accomplished, we add the two empty tables to the DataSet, using the Add function from the Tables collection, in the DataSet class.

 

We now need to relate the two tables, i.e. Customers and Orders. The field custID in the Customers table is unique for every customer record, and thus, is called the Primary Key. In the Orders table, the field cID is not unique, as a customer may place multiple orders. Thus, for every single customer in the Customer table, we may have multiple records in the Orders table. This type of relationship is called a parent-child relationship, or a primary key-foreign key relationship. A field in a table is called a foreign key, only if it is a primary key in another table, and if both the fields belong to the same domain. A DataRelation class recognizes a parent-child relationship.

 

The constructor of the DataRelation class requires three parameters:

1) The name of the DataRelation, which may be null.

2) The DataColumn object that represents the parent column. In our case, it is the field cCustID in the Customer table.

3) The child column, i.e. the field cID in the Orders table.

 

We now use the property named Relations in the DataSet class of type DataRelationCollection, to Add the relation to the Dataset. From now on, the DataSet class will relate each customer id from the Customer table to the multiple customer ids in the Orders table.

 

Now, the tables are required to be populated with some data. So, we start by creating a DataRow object, with the help of which, we shall populate the tables. Since we want to add three customers with IDs Cust 1, Cust 2 and Cust 3, we first call the NewRow function, which creates the DataRow object, and then we use the indexer with a column name to store the data. Using the Add member of the Row Collection, the row is then added.

 

Alternatively, we could have initialized the column custName in a for loop. This approach has been sidestepped or avoided since Microsoft samples have employed a different methodology. The Rows Collection object, denoted by the property Rows, has an indexer that facilitates access to each row.  For e.g. tCust.Rows[0] accesses the first row. The DataRow objects indexer can be utilized to change the field custName.

 

For each of the three customers, we would now want to add five records to the Order table. The Custid and the year of the order date, remain the same. The month number is suffixed with 1, 2 or 3, depending upon the customer. And the day is increased by 2, in consideration of each order. The amount on the order is, the month number multiplied by 10, plus the value of j in the inner for loop, multiplied by 0.1. This expression generates a unique value for each order. With the help of the for loop, the process of populating tables can become reasonably simpler, as against, writing the values individually. Thus, we have 3 records in the Customer table and 15 records in the Orders table, i.e. 5 per customer.

 

Finally, the controls are bound to the columns in the data table, using the function BindControls. In the first textbox, we display the field custName from the Customer table, which is present in the DataSource ds. Currently, it happens to be a DataSet and not a DataTable or a Collection Object.

 

The last parameter to the Binding object Constructor is tablename.fieldname. It is not merely a field name. It is because a DataSet consists of a collection of tables, and the same field name could be present in more than one table. Thus, we need to clearly identify the table from which the column has been obtained. The second textbox is bound to the customer id from the customer table. The DateTimePicker control behaves akin to other controls, when it comes to data binding. Instead of the Text property, we bind the control to the Value property.

 

The point of greater significance is that, while displaying orderdate, instead of specifying order.orderdate, we use customers.CustToOrders.OrderDate, which is the parent-table-name.relation-name.field-name. The motive behind this will be explained in a short while from now. The date is displayed using a more sophisticated control than a simple textbox.

 

Like before, we want to call the code for the fourth textbox, which displays data that can be edited. So, firstly we are required to create a Binding object b, and associate two functions with the Format and Parse events. The delegates are added before the Add function is called, to facilitate the addition of the bindings, since no formatting takes place when we change the current object in the DataSource.

 

The BindingContext object takes more than one indexer. Therefore, the datasource, i.e. a DataSet or a navigational path, is used as an indexer to refer to a specific BindingManagerBase. The above-mentioned object is mandatory in order to update the record pointer and to display all the data in the data source. This parameter may either contain merely a table name or a table name followed by a relation name, e.g. customers.custtoOrders. The first BindingMangerBase object, bmCustomers, facilitates movement through the three customer records, because we have supplied the table name Customer. The second one, bmOrders, will move through those records using the relation, since we have supplied the relation name. Thus, even though we have 15 records in the Orders table, we can see only 5 of them.

 

Thus, whenever we click on the first two buttons, we use the bmCustomers object's Position property to move from one record to another, up or down, without checking for errors. The next two buttons also use the Position property of the bmOrders object, depending upon the active customer id, thus, reducing the number of records.

 

Screen 5.7

 

The method DecimalToCurrencyString is called every time we desire to display a new value in the last textbox. The value returned by the DesiredType member of the ConvertEventArgs parameter, is checked with a string. If they do not correspond with each other, the program exits out. This is the only error check performed. Thus, we first ascertain whether we are allowed to convert from a decimal type in the original value, to a string or not. If the conversion is allowed, we call the ToString function with the formatting character 'c', to carry out the actual conversion to a Currency.

 

The method CurrencyStringToDecimal is called whenever we modify the value in the textbox. As earlier, we perform the same error check on the conversion to string, and then we use the Parse event to convert it into the original decimal type. If we change the display to 20.001, we may see it as 20.00. But the original value still remains at 20.001. The WriteLine function can be used to display the unformatted value.

 

Finally, the textbox control text4 displays the current customer id. If we move the top two buttons, the value contained in this textbox changes. However, when we alter the Position property on the relation, the customer id remains constant, while the other two fields change. This example illustrates how the data source can be made extremely complex using the BindingManagerBase class, which has its work cut out for it.

 

Screen 5.8

 

a.cs

using System;

using System.Drawing;

using System.Windows.Forms;

using System.Data;

using System.Data.SqlClient;

public struct State1

{

string shortName, longName;

public State1(string longName , string shortName)

{

this.shortName = shortName ; this.longName = longName ;

}

public string ShortName

{

get

{

return shortName;

}

}

public string LongName

{

get

{

return longName;

}

}

}

public class zzz : Form

{

int cnt;

DataSet customersDataSet1;

ComboBox comboBoxState;

TextBox textBoxPosition;

Button buttonMoveFirst,buttonMovePrev,buttonMoveNext,

buttonMoveLast;

TextBox textBoxID,textBoxRegion;

Label labelID;

Panel panelVCRControl;

// Washington not there

public State1[] States  = new State1[]

{

new State1("Alaska","AK"),new State1("California" ,"CA"),new State1("Idaho","ID"),new State1("Montana" ,"MT"),

new State1("New Mexico" ,"NM"),new State1("Oregon"  ,"OR")

,new State1("Wyoming" ,"WY")

} ;

public zzz()

{

buttonMoveLast = new Button();

customersDataSet1 = new DataSet();

buttonMoveFirst = new Button();

textBoxID = new TextBox();

textBoxRegion = new TextBox();

textBoxPosition = new TextBox();

buttonMovePrev = new Button();

panelVCRControl = new Panel();

comboBoxState = new ComboBox();

labelID = new Label();

buttonMoveNext = new Button();

buttonMoveNext.Click += new System.EventHandler(buttonMoveNext_Click);

buttonMoveNext.FlatStyle = FlatStyle.Flat;

buttonMoveNext.Location = new System.Drawing.Point(280, 8);

buttonMoveNext.Size = new System.Drawing.Size(32, 32);

buttonMoveNext.Text = ">";

customersDataSet1.DataSetName = "CustomersDataSet";

buttonMoveFirst.FlatStyle = FlatStyle.Flat;

buttonMoveFirst.Click += new System.EventHandler(buttonMoveFirst_Click);

buttonMoveFirst.Location = new System.Drawing.Point(8, 8);

buttonMoveFirst.Size = new System.Drawing.Size(32, 32);

buttonMoveFirst.Text = "|<";

textBoxID.Enabled = false;

textBoxID.Location = new System.Drawing.Point(88, 16);

textBoxID.ReadOnly = true;

textBoxID.Size = new System.Drawing.Size(299, 20);

textBoxRegion.Location = new Point(88, 116);

textBoxRegion.Size = new System.Drawing.Size(299, 20);

ClientSize = new System.Drawing.Size(464, 357);

Text = "Customer Details";

textBoxPosition.Enabled = false;

textBoxPosition.Location = new System.Drawing.Point(88, 14);

textBoxPosition.ReadOnly = true;

textBoxPosition.Size = new System.Drawing.Size(184, 20);

buttonMovePrev.Click += new System.EventHandler(buttonMovePrev_Click);

buttonMovePrev.FlatStyle = FlatStyle.Flat;

buttonMovePrev.Location = new System.Drawing.Point(48, 8);

buttonMovePrev.Size = new System.Drawing.Size(32, 32);

buttonMovePrev.Text = "<";

panelVCRControl.Location = new System.Drawing.Point(88, 288);

panelVCRControl.Size = new System.Drawing.Size(360, 48);

comboBoxState.Location = new System.Drawing.Point(88, 208);

comboBoxState.Size = new System.Drawing.Size(176, 20);

comboBoxState.Text = "";

labelID.Location = new System.Drawing.Point(16, 16);

labelID.Size = new System.Drawing.Size(64, 16);

labelID.Text = "ID:";

buttonMoveLast.Click += new System.EventHandler(buttonMoveLast_Click);

buttonMoveLast.FlatStyle = FlatStyle.Flat;

buttonMoveLast.Location = new System.Drawing.Point(320, 8);

buttonMoveLast.Size = new System.Drawing.Size(32, 32);

buttonMoveLast.Text = ">|";

panelVCRControl.Controls.AddRange(new Control[] {textBoxPosition,buttonMoveFirst,buttonMovePrev,

buttonMoveNext,buttonMoveLast});

Controls.AddRange(new Control[] {comboBoxState,panelVCRControl,textBoxID,labelID,textBoxRegion});

 

SqlConnection con = new SqlConnection("server=(local)\\NetSDK;

uid=QSUser;pwd=QSPassword;database=northwind");

SqlDataAdapter cmd = new SqlDataAdapter("Select * from Customers where country='USA'", con);

cmd.Fill(customersDataSet1, "Customers");

comboBoxState.DataSource=States;       

comboBoxState.DisplayMember="LongName";    

comboBoxState.ValueMember="ShortName";     

comboBoxState.DataBindings.Add("SelectedValue", customersDataSet1, "Customers.Region");

textBoxID.DataBindings.Add("Text", customersDataSet1, "Customers.CustomerID");

textBoxRegion.DataBindings.Add("Text", customersDataSet1, "Customers.Region");

BindingContext[customersDataSet1,"Customers"].PositionChanged += new System.EventHandler(customers_PositionChanged);

DataTableCollection tc = customersDataSet1.Tables;

DataTable t = tc[0];

cnt = t.Rows.Count;

textBoxPosition.Text = "Record " + (BindingContext[customersDataSet1,"Customers"].Position + 1) + " of " + cnt;

}

void buttonMoveFirst_Click(object sender, System.EventArgs e)

{

BindingContext[customersDataSet1,"Customers"].Position = 0 ;

}

void buttonMoveLast_Click(object sender, System.EventArgs e)

{

BindingContext[customersDataSet1,"Customers"].Position = cnt - 1;

}

void buttonMoveNext_Click(object sender, System.EventArgs e)

{

if (BindingContext[customersDataSet1,"Customers"].Position < cnt - 1)

{

BindingContext[customersDataSet1,"Customers"].Position++;

}

}

 

 

void buttonMovePrev_Click(object sender, System.EventArgs e)

{

if (BindingContext[customersDataSet1,"Customers"].Position > 0)

{

BindingContext[customersDataSet1,"Customers"].Position--;

}

}

void customers_PositionChanged(object sender, System.EventArgs e)

{

textBoxPosition.Text = "Record " + (BindingContext[customersDataSet1,"Customers"].Position + 1) + " of " + cnt;

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

Screen 5.9

 

Continuing with our saga on data handling, let us launch a few more intriguing twists and turns. As before, let us tackle the User Interface issues first. We possess one label, three textboxes, one combo box and four buttons. There is nothing novel or innovative about this. In the earlier programs, we had entered the data in our program itself, using a DataTable or a DataSet, and this data was eventually displayed. In this program, we source the data from a database.

 

When we install the .NET framework, a large number of databases are brought into existence and installed in SQL Server. To access the data within a database, we have to use the SqlConnection class. The constructor is given a string that identifies the machine on which the database server resides. The default in our case is server=(local)\\NetSDK, where 'local' represents the machine we are currently working on. The installation program creates NetSDK. The word following server is 'uid', which denotes the user name, and the word 'pwd' implies the password. The values supplied are QSUser and QSPassword, respectively.

 

All data is stored in tables that reside in a database. We are interested in a list of customers, which is stored in a table called Customers, residing in the northwind database. Therefore, in the connection string, we specify database = northwind.

 

The SqlConnection class is merely capable of comprehending the wherewithal of connecting to a database. It is clueless about the word SQL or Structured Query Language. SQL is a language used to extricate data from one or more tables. Thus, we introduce a new class named SqlDataAdapter that understands SQL, and then, we pass the SQL statement to its constructor along with the connection object. The SQL statement "Select * from Customers where country = 'USA' ", selects all the fields, since the symbol * represents all fields of the Customers table. The 'where' condition restricts/filters records whose country field has the value of 'USA'. As of now, no data gets retrieved. The class stores this information internally.

 

It is the Fill command of the SqlDataAdapter class, which is responsible for filling up the DataSet customersDataSet1. The first parameter supplied is the DataSet and the second parameter is a tablename whose fields are to be mapped. The tablename has to be a valid table name, or else, a run time exception will be generated. The return value is the number of rows that are present in the data source. Earlier, we had used the 'for' statements to provide data; whereas, currently we are using real life data from a database to load a table. The WriteLine function if given here would display the number of records in the DataSet, which is 13 in this case.

 

The DataSource property in the Combo Box control is of type object. This property determines what the combo box displays. Here, we have specified an array called States, of data type State1. Our array contains seven members of type State1.

 

The State1 object has two members, viz. longName and shortName. LongName stores the actual name of the state and shortName stores the two-character abbreviations. The constructor of the class initializes these two members.

 

It is our misfortune that, despite being tantalized by being offered the exciting privilege of selecting names for parameters, the same old names as assigned to the fields, are being ascribed to the parameters as well. Therefore, to access the field shortName from within the constructor, we need to preface it with the word 'this'. The 'this' keyword is optional in situations where we use different names for the parameters. Thus, the combo box shall display one of the values present in the States array, which encompasses the short and the long names of seven different states. To authenticate this, you may click on the down arrow and see the names of the seven states.

 

Screen 5.10

 

The question that comes to the fore at this stage is that, 'How does the combo box come to a decision on the values to be displayed?' The combo box or any control that displays a list, takes two different values, namely, DisplayMember and ValueMember. DisplayMember takes a decision on the data that the user sees in the list box, while ValueMember is the actual value of the selected item.

 

Thus in our case, we get to see the full name of the state, when the DisplayMember property is initialized to longName. The ValueMember is equated to the shortName. So, the value obtained from this list box is the abbreviated name of a state. These two have to be properties in the class State1.

 

The combo box, like all other controls, has a Binding property. The first two textboxes are bound to the CustomerID field and the Region field. They are preceded by the table-name. The name of the DataSet is also specified. The field from the dataset is the Region field that contains the abbreviation, but we get to see the full name, since the DisplayMember is longName.

 

Let us now work under the assumption that the current value of the region field is NM. So, the combo box shall display New Mexico, and not NM.

 

Screen 5.11

 

The framework embarks on its search from the beginning of the States array, and then calls the property shortName. It verifies every entry in the array, in order to confirm whether the value is 'NM' or otherwise. Once the value matches, it calls the property longName to display the full name in the combo box.

 

Thus, if the desired shortName were at a position that is deep down the array, it would entail summoning the shortName property numerous times. The long name property, for reasons unknown, gets called only twice. The state Washington is not present in the array. In a situation like this, the framework moves through all the members in the array. If no match is found, it displays the first member of the array, which in this case is Alaska.

 

To unravel the number of records in the table, we first need to access the DataTableCollection object. This is accomplished using the Tables property, which results in a collection. Thereafter, the tables are accessed, using the indexer. In order to access the table Customers, tc[0] is used. Subsequently, the Count property of the Rows collection is used to return the number of rows in the table.

 

The Binding Manager code, which is essential to move the active object, remains the same. So, we shall not delve upon it any further. All that we are trying to explain through the above example is that, by using one value in a database, we can display another value.

 

You need to commit it to your memory that, we are binding to a combo box for the first time ever. The combo box is bound to the SelectedValue property. If you comment out this binding, you will observe that the values remain impervious or unaltered. Further, if you click in the combo box, you will witness the display of the names of all the states.

 

Data Grids

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

using System.Data.SqlClient;

public class zzz : Form

{

DataGrid d;

DataSet c;

public zzz()

{

d = new DataGrid();

d.Size = new Size(584, 336);

d.DataMember = "Customers1";

ClientSize = new Size(600, 413);

SqlConnection con = new SqlConnection("server=(local)\\NetSDK;

uid=QSUser;pwd=QSPassword;database=northwind");

SqlDataAdapter Cust = new SqlDataAdapter ("Select * from Customers", con);

c = new DataSet();

d.DataSource = c;

Cust.Fill(c, "Customers1");

d.DataMember = "Customers1";

Controls.Add(d);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

One of the most common uses of data is to position it in a tabular form. This format can be achieved by using a DataGrid control, which is a collection of columns and rows. The above program displays data from the Customer table in a data grid object.

 

Screen 5.12

 

We commence by creating a DataGrid object d, and assign it a certain size using the Size property. As earlier, we create a SqlConnection object to connect to the database server on a machine, and then, create a SqlDataAdaptor object Cust to represent all the data from the Customers table.

 

The DataSource property of the DataGrid is initialized to a freshly created, albeit, empty DataSet object, c. This is because a DataGrid displays data from a source, and therefore, the DataSource property is specifically introduced to identify the source. Dataset is not the only medium, since the data source can obtain a value from seven different entities. We shall explore this in greater detail in the next example.

 

Using the Fill function of the SqlDataAdaptor class, the DataSet 'c' is packed with data. You can assign any name to it. We have used Customer1 mainly to facilitate source mapping. Bear in mind that the DataSet, by itself, is devoid of any data. It comprises of other sources, which in turn, contain data. This highlights the fact that a DataSet contains supplementary data sources. This function is obtained from the DbDataAdapter class.

 

Finally, the DataSource for the DataGrid class is to be specified. As we have only one source i.e. Customers1, we have initialized the DataMember property to it. Had the DataMember property not been supplied with a value, i.e. d.DataMember = "", we would not have seen any data on start up. At this stage, a plus sign would be displayed in the empty grid. Clicking on the plus sign would then show the name of our solitary source, Customer1. This is displayed as a hyperlink. When we click on the link, in addition to the new dataset being displayed in the heading of the data grid, the previous data is also displayed.

 

Screen 5.13

Screen 5.14

 

If we add the line Cust.Fill(c, "Customers2") immediately after the first Fill function, it results in the creation of a second source. Thus, at this stage, two sources are present.

 

Screen 5.15

 

Therefore, clicking on the + sign in the DataGrid at this stage would display two different sources as hyperlinks. Clicking on either of the hyperlinks will exhibit the same set of data. The point worthy of notice is that, we can let the user dynamically choose the source that he wishes to work with.

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

using System.Data.SqlClient;

public class zzz : Form 

{

DataGrid d;

public zzz()

{

d = new DataGrid();

d.Size = new Size(584, 336);

ClientSize = new Size(600, 413);

DataTable t = new DataTable("Control");

t.Columns.Add("Name");

t.Columns.Add("City");

DataRow r;

r = t.NewRow();

r["Name"] = "Vijay";

r["City"] = "Bombay";

t.Rows.Add(r);

r = t.NewRow();

r["Name"] = "Sonal";

r["City"] = "Delhi";

t.Rows.Add(r);

d.DataSource = t;

Controls.Add(d);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

As we had mentioned earlier, the data source should essentially be dynamic since data comes in different shapes and sizes.

 

In this program, we create a simple DataTable t with two columns named Name and City. This DataTable is then supplied as the DataSource. Since we are making use of an entity that contains data, we do not have to specify the DataMember explicitly.

 

Screen 5.16

 

This results in the display of two records in the DataGrid control.

 

The DataTable could also have been initialized by sourcing data from a database using the SqlDataAdaptor class.

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

using System.Data.SqlClient;

using System.Collections;

public class CustomerList : System.Collections.CollectionBase {

public static CustomerList GetCustomers() {

CustomerList cl = new CustomerList();

Customer a = Customer.ReadCustomer1();

IList b = cl.List;

b.Add(a);

Customer c = new Customer("246-12-5645");

c.FirstName = "Vijay";

c.DateOfBirth = DateTime.Parse("5/3/1933");

cl.Add1(c);

cl.Add1(Customer.ReadCustomer2());

return cl;

}

public int Add1(Customer value) {

return List.Add(value);

}

}

public class Customer {

public string id,FirstName ;

public DateTime dateOfBirth;

public static Customer ReadCustomer1()

{

Customer cust = new Customer("536-45-1245");

cust.FirstName = "Sonal";

cust.DateOfBirth = DateTime.Parse("9/9/1941");

return cust;

}

public static Customer ReadCustomer2()

{

Customer cust = new Customer("651-27-8117");

cust.FirstName = "Manish";

cust.DateOfBirth = DateTime.Parse("3/25/1942");

return cust;

}

public Customer(string ID): base()

{

id = ID ;

}

public string ID

{

get  

{

return id ;

}

}

public string FirstName1

{

get 

{

return FirstName ;

}

}

public DateTime DateOfBirth

{

get 

{

return dateOfBirth ;

}

set

{

dateOfBirth = value ;

}

}

}

public class zzz : Form {

DataGrid d;

public zzz() {

d = new DataGrid();

d.Size = new Size(584, 336);

ClientSize = new Size(600, 413);

CustomerList custList;

custList = CustomerList.GetCustomers();

d.DataSource = custList ;

Controls.Add(d);

}

public static void Main() {

Application.Run(new zzz());

}

}

 

A DataSource for a DataGrid can comprise of seven different types of Data sources. These are as follows:

     DataTable

     DataView

     DataSet

     DataViewManager

     Single dimensional array

     IList interface

     An object that implements the IListSource

 

Screen 5.17

 

The above example is merely a replica of the first example of this Chapter, where we had used a class derived from CollectionBase. The same rules as mentioned in the first example shall be applicable here also.

 

For e.g. the presence of properties that represent column names, etc. As we have only three properties, only three columns are displayed in the DataGrid. This program also illustrates the utilization of the varied data sources in a data grid.

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

using System.Data.SqlClient;

public class zzz : Form {

DataGrid d;

DataSet c;

public zzz()

{

d = new DataGrid();

d.Size = new Size(584, 336);

d.DataMember = "Customers1";

ClientSize = new Size(600, 413);

SqlConnection con = new SqlConnection("server=(local)\\NetSDK;

uid=QSUser;pwd=QSPassword;database=northwind");

SqlDataAdapter Cust = new SqlDataAdapter ("Select * from Customers", con);

c = new DataSet();

d.DataSource = c;

Cust.Fill(c, "Customers1");

d.DataMember = "Customers1";

d.AlternatingBackColor = Color.Red;

d.BackColor = Color.Blue;

d.BackgroundColor = Color.Green;

Rectangle r = d.Bounds;

System.Console.WriteLine(r);

r = new Rectangle(1,100,200,400);

d.Bounds = r;

Controls.Add(d);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

Screen 5.18

Screen 5.19

 

In the above example, we have a large number of properties whose values can be altered. Within a DataGrid, if we click on a column, a sort is performed on the values in the column. If the column is clicked again, the sort order is reversed. The column which decides the sorting has an arrow displayed next to it.

 

Sorting is enabled by default and can be disabled by initializing the property AllowSorting to False. However, there is no way of disabling the facility to sort on a single column. Provision is available to sort on an expression.

 

The property AlternatingBackColor bestows a ledger-like appearance to our Grid. The background color of every alternate row is of a specific shade. In this case, it is red. The BackColor property of the DataGrid control, which bestows every row with the same background color, is set to blue. The default color for this property is the system color of Windows. Setting the BackColor property to Color.Empty switches the color mode to the default color.  Thus, we observe that the first row and every alternate row thereafter are blue in color, and the remaining rows are red in color.

 

There is a narrow tract at the bottom of the grid, which is displayed in green color. This color is determined by the value assigned to the property BackgroundColor. This narrow strip, which is part of the non-row area of the grid, is distinctly visible when the grid comprises of only a few rows, or when there is no table to be displayed in the grid.

 

The size of the DataGrid control can be controlled programmatically. The Bounds property in the DataGrid is a read-write property. Hence, the default rectangle structure, which contains the DataGrid, can be displayed using the WriteLine function. The X and Y co-ordinates are specified as 0,0, the Width is 584 pixels and the Height is 336 pixels.

 

Thus, it is evident that we have the discretion to determine the size of the DataGrid, since we have to share real estate on our Window with other controls.

 

a.cs

using System;

using System.Data;

using System.Drawing;

using System.Windows.Forms;

using System.Data.SqlClient;

public class zzz : Form

{

DataGrid d;

DataSet c;

public zzz()

{

d = new DataGrid();

d.Size = new Size(584, 336);

d.DataMember = "Customers1";

ClientSize = new Size(600, 413);

SqlConnection con = new SqlConnection("server=(local)\\NetSDK;uid=QSUser;

pwd=QSPassword;database=northwind");

SqlDataAdapter Cust = new SqlDataAdapter ("Select * from Customers", con);

c = new DataSet();

d.DataSource = c;

Cust.Fill(c, "Customers1");

d.DataMember = "Customers1";

d.CaptionForeColor = Color.Blue;

d.CaptionBackColor = Color.Red;

d.CaptionText = "Vijay Muhki";

Font f = new Font("Arial",10);

d.CaptionVisible = true;

Rectangle r = d.ClientRectangle;

System.Console.WriteLine(r);

Size s = d.ClientSize;

System.Console.WriteLine(s);

System.Console.WriteLine(d.Height + " " + d.Width);

System.Console.WriteLine(d.Left + " " + d.Right);

Point p = d.Location;

System.Console.WriteLine(p.X + " " + p.Y);

System.Console.WriteLine(d.Size);

d.ColumnHeadersVisible = false;

System.Console.WriteLine(d.CompanyName);

Control.ControlCollection cc =  d.Controls;

System.Console.WriteLine(cc.Count);

Control c1,c2;

c1 = cc[0]; c2 = cc[1];

System.Console.WriteLine(c1);

System.Console.WriteLine(c2);

System.Console.WriteLine(d.HasChildren);

Controls.Add(d);

}

public static void Main()

{

Application.Run(new zzz());

}

}

 

Output

{X=0,Y=0,Width=584,Height=336}

{Width=584, Height=336}

336 584

0 584

0 0

{Width=584, Height=336}

Microsoft Corporation

2

System.Windows.Forms.HScrollBar, Minimum: 0, Maximum: 100, Value: 0

System.Windows.Forms.VScrollBar, Minimum: 0, Maximum: 0, Value: 0

True

 

A caption, which is similar to a Windows title, is displayed above the column names. It furnishes additional information to the user. The text to be displayed is decided by the CaptionText property, which by default is an empty string.

 

Screen 5.20

 

The CaptionForeColor property determines the foreground color of the caption, while the CaptionBackColor decides on the background color. The CaptionFont property refers to the font in which the text is to be displayed. The boolean value in the property CaptionVisible exhibits or suppresses the display of the caption.

 

There are a large number of properties for a Caption since it is derived from the base class of Control. The documentation specifies whether the property is read-write or not, thereby signifying whether change is permitted at the design stage or not.

 

The ClientRectangle property, which is read-only, displays the same results as