c# classes

207
C# - Classes Contents Authors Introduction Section I - Threads 1. Threads, Events and Mutextes 2. Internet Classes 3. Remoting Section II - Winforms 4. Controls 5. Data Handling 6. Printing Section III - Xml 7. Xml Classes 8. The DTD 9. Xml Data Document 10. Xml Documentation

Upload: tiago

Post on 23-Jan-2017

18 views

Category:

Education


0 download

TRANSCRIPT

Page 1: C#   classes

C# - Classes

Contents

Authors Introduction Section I - Threads 1. Threads, Events and Mutextes 2. Internet Classes 3. Remoting Section II - Winforms 4. Controls 5. Data Handling 6. Printing Section III - Xml 7. Xml Classes 8. The DTD 9. Xml Data Document 10. Xml Documentation

Page 2: C#   classes

Authors Vijay Mukhi ([email protected]) is one of the pioneers of the Indian Infotech Industry. For years, he has been the first to teach the emerging technologies in India thus ensuring that India always has people trained in technologies that the world requires. Vijay has written over 80 books on computers and programming over the last eight years on subjects ranging from C, C++ (The Odyssey Series) to Animation and Networking to Java to C#. Vijay abhors complexities and hence his books showcase the most difficult concepts explained through small programs, thereby giving a good understanding. Microsoft's .Net technologies is what Vijay is now focusing on and he aims at writing volumes on it. His knowledge on .Net Technologies is clearly visible in his four books where he explains the different concepts of the C# programming language in the most simplified form. Vinay Kalantri ([email protected]) is a bright and enterprising software expert who has completed an extensive course in Business Management from a reputed University in the United States of America. He is presently undergoing rigorous training in software development in frontline applications. Sonal Mukhi ([email protected]) is a freelance programmer having a widespread exposure to computer techniques and languages. Sonal has done groundbreaking work on various Internet Technologies like Java, ActiveX, Perl, C# and more. She has co-authored a few books on computer programming too.

Introduction This book presents myriad fascinating concepts about C# classes. It is classified into three sections, with each of them converging on distinct facets of classes available in the .Net framework The curtains are raised with the chapter on Threads in Section I, which presents a nascent introduction to this topic. The topics of Events and Mutexes are also explored in detail. The next chapter is on the Internet related classes. Here, the utility of the Web classes and their role in building server and client applications are highlighted. The last chapter in this section takes on a different hue. It encapsulates the crux of writing programs to implement the concept of 'remoting'. This involves the composition of a client and a server program on different machines. The spotlight then moves on to the concept of Winforms in Section II. This chapter is replete with practical and useful insights into how Winforms can be put to optimum use in the .Net world. The C# language provides the facility of generating user-interfaces having aesthetic appeal, either by implementing the ready-to-use Form Controls, or by using controls crafted by us, in order to receive inputs from an end user. The subsequent chapter in this section delves upon the Database Controls. This chapter has been sedulously crafted, to capture the essence of this significant activity and to reveal its intricate details. The ubiquitous task of printing is the focus of the next chapter. The contents of this chapter will equip you to print output with practiced panache. The last section is devoted to XML, which is the most hyped language in the current market scenario. An XML file embodies an assortment of components, which will be unraveled, one at a time, in the chapters of this section. At the outset, the common XML classes have been highlighted, to provide an introduction to XML concepts, which are used while programming in the C# language. The next two chapters focus on DTD, which is an acronym for Data Type Definition, and also on the XML Data Document. They provide information on the various elements employed while generating an XML file. The topic of XML Documentation provides a hiatus from the preceding abstruse topics. It has been laced with effervescent text and examples to liven up the proceedings. We have applied utmost perspicacity to ensure that accurate, useful and relevant explanations, laced with lucid and practical examples, be presented to expound the various concepts to both, the amateur and the proficient programmer alike. We assure you that, by the time you disembark from this intellectual voyage of discovery, the various concepts that have been presented, are doubtlessly bound to create an indelible imprint on your minds. Requirements The software requirements to successfully run all the programs in this book are

Page 3: C#   classes

• Operating System - Windows 2000 • A Web Server preferably Microsoft IIS ver 5.0• SQLServer 2000 (Evaluation Edition)• Internet Explorer 5.5• .Net Framework SDK Beta 2 (111 MB)

Internet Explorer 5.5 can be downloaded off the Microsoft site

• http://www.microsoft.com/windows/ie/download/ie55sp1.htm Net Framework SDK Beta 2 can be downloaded off the Microsoft site

• http://download.microsoft.com/download/VisualStudioNET/Trial/2.0/W982KMeXP/EN-US/setup.exe Alternatively, you can visit the download section at Microsoft ( http://msdn.microsoft.com/downloads/default.asp ) and download the .Net framework SDK Beta 2 under the Software Development Kit option. SQLServer 2000 While installing the evaluation edition of SQLServer 2000, we have chosen the default selected settings. The only modification made is in the Authentication Dialog Box. You see two options

• Windows Authentication mode • Mixed mode (Windows Authentication and SQL Server Mode)

The default option selected is Windows Authentication mode. We have instead selected Mixed mode (Windows Authentication and SQL Server Mode). Once this option is selected, the password text boxes for the 'sa' user gets activated. As we would prefer using a blank password in our programs, we select Blank Password. On selecting this option, the text boxes get disabled again. Acknowledgements The many individuals who have worked together to produce this great work of art and motivated me all the way need a mention here. Their bright ideas, inspiration, support has made me a lot more stronger and wiser. First and foremost, thanks to Manish Jain, BPB Publications for publishing the book. To my co-authors, Sonal and Vinay who have put in a lot of hard work to complete the work assigned to them. To Tanuja Sodhi, an ex-Naval Officer from the first batch of lady officers and an MBA from Jamnalal Bajaj, for editing the book. She is presently freelancing as a creative writer. To Altaf Hemani and Kishore Rohra, for their creativity in designing the cover in a given short time. Thanks to Manish Purohit for putting in all the time he had to verify the code with the explanations and then giving the book a good look and feel. To Pradeep Mukhi and Shivanand Shetty, who have always been there, as a source of inspiration and encouragement. My Mother, Shana Aunty and a long list of friends need a mention here for their patience and cooperation on this book while it was being written.

-Vijay Mukhi

Page 4: C#   classes

1 Threads, Events and Mutexes Threads operate on the premise that the computer is definitely more expeditious than a human at executing tasks, resulting in the computer idling away most of its processing time, waiting for the human operator. Threads are a means of overcoming this wastage of processing time. They perform multiple tasks on a computer in rapid succession, thereby, creating the illusion of these tasks being executed simultaneously. No application can ever be comprehensive without employing threads. Before we try to infer what a thread does in the programming context, let us rummage through a few examples given below. Without being discursive, let us venture out on our odyssey of understanding the concept of a thread with the assistance of a very diminutive program.

a.csusing System.Threading;public class yyy{public static void abc(){System.Console.WriteLine("Hi");}}public class zzz{public static void Main(){ThreadStart ts = new ThreadStart(yyy.abc);Thread t = new Thread(ts);System.Console.WriteLine("Before Start");t.Start();}}

OutputBefore StartHi

This one is bound to leave you astonished because, we had earlier talked about starting out with a 'dimunitive' program. However, by no stretch of the imagination can the above program qualify as miniscule. Besides, the only work accomplished by this function is that it calls the static function abc, which in turn displays 'Hi'. In Main, we create an object ts, which is an instance of the class ThreadStart, which is derived from Delegate. Therefore, even though ThreadStart is a class, it also happens to be a delegate, whose constructor is given a static function called abc. Function abc is placed in the yyy class so that other classes can also use it. The program will work in a similar manner even if the static function is placed in class zzz. Next, we create another object t, which is an instance of Thread. The constructor of this object is given a ThreadStart object ts. Indirectly, ts stands for the static function yyy.abc since it is a delegate. So far, tranquility prevails and nothing transpires. The function yyy.abc too does not get called. But, as soon as we call Start off the thread, the function abc gets catapulted into action. Thus, the function abc is called only when the Start function is called. This is really no big deal. Note that classes beginning with Thread belong to the System.Threading namespace.

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("Hi");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));

Page 5: C#   classes

t.Start();}}

OutputHi

The above program is similar to the previous one and resembles the samples supplied by Microsoft. The function abc to be called, is non-static and hence, an object name is needed to reference it. The ThreadStart delegate object is directly passed as a parameter to the Thread constructor.

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3;i++){System.Console.Write(i + " ");}}public void pqr(){for ( int i = 0; i<=3;i++){System.Console.Write(i+ "...");}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.pqr));t.Start();t1.Start();}}

Output0 1 2 3 0...1...2...3...

The example embodies a very large amount of code. However, it does not create any fresh ripples in our pond of knowledge. We have merely created two Thread objects t and t1, and passed their constructors a different delegate or function name, i.e. abc and pqr, respectively. Thereafter, the Start function has been called in t and t1. Here, the function abc gets called, which displays four numbers. Thereafter, the function pqr gets called, which also displays four numbers, but with three dots. You may wonder with trepidation as to when you will bite into the real meat. Keep your impatience in abeyance for a little while !

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3;i++){System.Console.Write(i + " ");Thread.Sleep(1);}}public void pqr(){for ( int i = 0; i<=3;i++){System.Console.Write(i+ "...");Thread.Sleep(1);}

Page 6: C#   classes

}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.pqr));t.Start();t1.Start();}}

Output0 0...1 1...2 2...3 3...

Now, we are at the threshold of excitement. By adding the function Sleep, which is a static function in the Thread class, the functions abc and pqr get called simultaneously, although not sequentially. This is how the concept of a thread is implemented, i.e. it empowers your computer to accomplish multiple jobs at the same time. This process is also termed as multi-tasking. Take the case of Microsoft Word. When you attempt to save a file, irrespective of the size of the file, Word appears to consume the same amount of time in completing this task. To expect it to save a 10MB file within a very short time period is a thought fraught with absurdity and contrary to reason. Actually, what the Word program does is that it creates a thread and then solicits the thread to save the file. While the thread is saving that file, another thread waits for the user to type something. Hence, the user is emancipated to continue working with Word. Thus, in effect, two tasks are being executed concurrently without the user being cognizant of it ! The same mechanism is employed by Excel and other similar products. Any application under Windows runs in its own thread. Thus, if two programs are launched, there will be two threads running. The operating system refrains from playing favorites and gives an equal amount of time to each thread to execute. If there are two threads co-existing, then, out of every minute of processing time available, each of them will be allotted 30 seconds to execute. If a third program is now executed, a new thread will be launched and each of the three threads will be allotted only 20 seconds of the processor time per minute to execute. If, instead of running a third program, what if the second program itself creates a thread ? As before, this will result in the creation of a third thread and each thread will be allotted 20 seconds of time. The resultant effect would be that that the first program will be allotted 20 seconds of time, whereas the second program will get 40 seconds per minute of the processor time. Thus, the larger number of threads that a program creates, more will be the processor time allotted to it. If you crave for more time and attention from your computer, desist from throwing a tantrum. Instead, generate greater number of threads. The computer allots a specific amount of time to each thread and then puts it to sleep. Thereafter, it executes another thread. The time given to each thread to execute its code is designated as a Time Slice. This allocation of Time Slices occurs so swiftly that each thread suffers from the hallucination that it enjoys the undivided attention of the computer. The static function Sleep in the Thread class facilitates this process. Sleep is like the sandman. It puts the thread to sleep for a certain number of milliseconds, as specified in its parameter. In this case, it is 1 millisecond. In the earlier example, in a single time slice, the Thread executed all the code and the function completed execution. In this case, the Sleep delays it long enough for the next thread to execute, and thus, the code in the functions get called one after the other.

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3;i++){Thread t2 = Thread.CurrentThread;System.Console.Write(i + "." + t2.Name + " ");Thread.Sleep(1);}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Name="One";

Page 7: C#   classes

t1.Name="Two";t.Start();t1.Start();}}

Output0.One 0.Two 1.One 1.Two 2.One 2.Two 3.One 3.Two

Here, we have provided mental catharsis by simplifying the program. What we actually have done is given the same function name abc to our delegate. We have also used the property Name of the Thread class to assign a distinct name to each thread, i.e. One and Two respectively. Under normal circumstances, the names assigned are pre-ordained by the system. Now, both the threads will call the same function abc. How do we determine as to which thread is calling the function abc? The Thread class can have members, which are either static or instance. CurrentThread is a static read-only property that returns a thread object, which represents the thread that has currently called the function. Here, Thread t2 will either represent t or t1, and the property Name will display the name of the thread executing the function.

a.csusing System.Threading;public class zzz : Thread{}

Compiler Errora.cs(2,14): error CS0509: 'zzz' : cannot inherit from sealed class 'System.Threading.Thread'

The Thread class is a sealed class, therefore we cannot derive from it. The designers of the Thread class at Microsoft have held very doctrinaire opinions, in that, they believe that they have envisaged all the possible features required in this class. Thus, they have not provided any facility to override or modify it. Therefore, we are constrained to use the Thread class exactly as provided, without introducing any code that will complement or add to or subtract from the code of the Thread class. For your information, the Thread class in turn is derived from the interface ISerializable.

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("Hi");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));System.Console.WriteLine(t.IsAlive);t.Start();System.Console.WriteLine(t.IsAlive);t.Abort();System.Console.WriteLine(t.IsAlive);}}

OutputFalseTrueHiFalse

The Thread class has a property called IsAlive that reveals the current state of the Thread. When we create an object that looks like Thread, the thread is in a dead state and hence, the IsAlive property has a value of False. Start breathes life into the thread and executes through the delegate abc. The thread now comes alive, but the code in the thread function gets executed only after the subsequent WriteLine function, which displays the value of IsAlive as True. When we stop or Abort the Thread, the thread dies and the value of the property IsAlive reverts back to False. Thus, IsAlive can hold only one of the two values, False for dead or True for alive.

a.csusing System.Threading;public class yyy{

Page 8: C#   classes

public void abc(){System.Console.WriteLine("Hi");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();while ( !t.IsAlive )System.Console.WriteLine("hi1");}}

OutputHi

In today's world, we are working on extremely fast machines. When we execute t.Start(), the thread is brought to life in a fraction of a second. Hence, the while loop is not executed because the condition becomes false immediately. Had the machine been slower, the while loop may have got called a few times, as it would have taken some time before the thread could have been brought to life. Thus, the speed of execution may vary, depending upon the speed of the machine. Whenever the sample code creates a thread, it always contains this while loop.

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3 ; i++)System.Console.Write("Hi " + i + " ");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();System.Console.WriteLine("Over");}}

OutputOverHi 0 Hi 1 Hi 2 Hi 3

The executable, a.exe, runs in its own thread. So, before calling the function Start, we already have one thread running. On calling the function Start, two threads run concurrently, independent of each other. The second thread executes the function abc independent of the first thread. The first thread executes the last WriteLine function and then stops, whereas the second thread continues executing the function abc till there is no more code to call. If, on your machine, the first thread's time slice gets over before executing the WriteLine function, then some code of the function abc may get executed before Over gets displayed in the function Main. It is reiterated yet again that, in case of threads, there can be no guarantee as to when the time slice of a thread will get over. Even the operating system is not sagacious enough to offer any guarantees in this regard.

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3 ; i++)System.Console.Write("Hi " + i + " ");}}public class zzz{public static void Main()

Page 9: C#   classes

{yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();t.Join();System.Console.WriteLine("Over");}}OutputHi 0 Hi 1 Hi 2 Hi 3 Over

The function Join is a blocking function. It makes the program linger at the function Join till the thread finishes execution. Any code after the Join, in this case the WriteLine function, will be executed only after the thread dies. A blocking function will wait until the purpose it is waiting for has reached fructification. Thus, if we want to wait for a thread to complete execution, we use a Join. Join behaves like a party host, who is always the last to leave.

a.csusing System.Threading;public class yyy{public void abc(){for ( ; ; ) ;}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();bool b = t.Join(10000);System.Console.WriteLine("Over " + b);}}

OutputOver False

The Join function accepts a number as a parameter, which represents the maximum duration in milliseconds that it should wait for the thread to complete execution. In our example, the function abc will never end. Thus, the thread t will go on forever. Therefore, the Join function waits for 10 seconds, gives up and finally returns a False.Therefore, we are empowered to decide the duration for which we want to wait for the thread to complete execution. Let us not forget that our application has not yet terminated and is still hanging around in memory. At this stage, if you press Ctrl-Alt-Del, you will see a list of programs running. Now, select the End Task option for the program called 'a'. An alternative approach could be to add the Abort function t.Abort() after the WriteLine function.

a.csusing System.Threading;public class yyy{public void abc(){for ( ; ; ) ;}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Join();}}

OutputUnhandled Exception: System.Threading.ThreadStateException: Thread has not been started. at System.Threading.Thread.Join() at zzz.Main()

If there is no thread running, the Join function will throw the exception of ThreadStateException.

Page 10: C#   classes

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("abc");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();Thread.Sleep(3);t.Abort();System.Console.WriteLine("Over");Thread.Sleep(100);t.Start();}}

OutputabcOverUnhandled Exception: System.Threading.ThreadStateException: Thread is running or terminated. Cannot restart. at System.Threading.Thread.StartInternal(IPrincipal principal, StackCrawlMark & stackMark) at System.Threading.Thread.Start() at zzz.Main()

At first, we start the thread t by calling the Start function. Thereafter, we make the main thread sleep for a little while. Then, we abort the thread and again sleep for a little while, in order to enable the thread to fulfill its last wishes and finally die. Now that the thread is dead, we try and infuse life into it by calling Start again. A thread, which has died cannot be resuscitated. Since we have had the audacity to attempt this, the results are nearly cataclysmic, resulting in the generation of an exception which has to be caught.

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("abc");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();Thread.Sleep(3);t.Abort();System.Console.WriteLine("Over");Thread.Sleep(100);try { t.Start(); } catch (ThreadStateException e) { System.Console.WriteLine("In Exception"); } }}

Output

Page 11: C#   classes

abcOverIn Exception

The exception thrown is ThreadStateException, which needs to be caught in our code. Otherwise, the runtime message box is displayed to the user. The Thread class constructor can throw two types of exceptions:-

• ArgumentNullException - When it is called without a delegate in the constructor. • SecurityException - When the program does not have permission to create a thread.

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("abc");}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));System.Console.WriteLine(t.ApartmentState);t.ApartmentState = ApartmentState.STA;t.Start();System.Console.WriteLine(t.ApartmentState);}}

OutputUnknownSTAabc

We all have our own apartments, which are normally done up as per our preferences i.e. 'We are the kings of our castles'. Threads also have apartments. Any thread can be asked to execute either in a single-threaded apartment or in a multi-threaded apartment. These values can be established using the property ApartmentState, either once at the beginning, or while the thread is running. The various values that ApartmentState can assume are as follows:

· STA : Single Threaded Apartment.· MTA : Multi Threaded Apartment. · Unknown : Default value assigned when no value is set.

We, however, cannot use numbers for a string. Thus, an enum called ApartmentState has been used. It holds three values i.e. 0, 1 and 2 corresponding to STA, MTA and Unknown respectively.

a.csusing System.Threading;public class yyy{public void abc(){System.Console.WriteLine("abc " + Thread.CurrentThread.IsBackground);}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));System.Console.WriteLine("Main " + Thread.CurrentThread.IsBackground);t.Start();}}

Output

Page 12: C#   classes

Main Falseabc False

A thread executes either in the background or in the foreground. The property IsBackground indicates the mode in which the thread will run. An important point to be noted here is that, a thread which executes in the background automatically shuts down when its main program quits.

a.cs using System.Threading;public class yyy{public void abc(){for ( int i = 0 ; i<=100; i++){System.Console.WriteLine("abc " + i);Thread.Sleep(1);}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));System.Console.WriteLine("Main " + Thread.CurrentThread.IsBackground);t.IsBackground = true;t.Start();Thread.Sleep(10);}}

Output (t.IsBackground = true)Main Falseabc 0

Output (t.IsBackground = false)Main Falseabc 0abc 1abc 2....abc 100

When you change the true to false in t.IsBackground, the output shows the code of function abc being executed. The for loop displays values from 0 to 100. By changing the property IsBackground of a thread to true, the thread terminates when the main program stops. Thus, the for loop does not have the time to display values upto 100. At times, the program displays abc with a value of 0. The sleeps are used to demonstrate the effect of the first thread shutting down and the second carrying on execution. On changing the property IsBackground to false, the thread takes its own sweet time to execute its code without any regard for the first thread, which may or may not have completed its job. So, the foreground threads behave exactly in the same manner as the background threads, when the property is initialized to true.

a.cs using System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=10;i++){System.Console.Write(i + " ");Thread.Sleep(1);}}public void pqr(){

Page 13: C#   classes

for ( int i = 0; i<=10;i++){System.Console.Write(i+ "...");Thread.Sleep(1);}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.pqr));System.Console.WriteLine(t.Priority);t.Priority = ThreadPriority.Highest;t1.Priority = ThreadPriority.Lowest;t.Start();t1.Start();}}

OutputNormal0 1 2 3 4 0...5 1...6 2...7 3...8 4...9 5...10 6...7...8...9...10...

We are at liberty to decide the duration of the time-slice to be allotted to our thread vis-a-vis other threads. This can be achieved by setting the Priority property accordingly. By default, the priority level is 2. Hence, we see 'Normal' displayed as the output. All threads start at this priority level. The ThreadPriority enum comprises of five levels of priority, which are as follows:

• 0 - Zero• 1 - BelowNormal• 2 - Normal• 3 - AboveNormal• 4 - Highest

In our program, we have changed the priority of thread t to the highest possible and that of thread t1 to the lowest. As a result, Thread t is accorded more time than thread t1, and it finishes its for loop much before thread t1 can do so.

a.cs using System.Threading;public class yyy{public void abc(){try{System.Console.WriteLine("in abc");for ( ; ; ) ;}catch ( System.Exception e){System.Console.WriteLine("in abc Exception " +e.ToString() );}finally{System.Console.WriteLine("in abc Finally");}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();Thread.Sleep(10);t.Abort();}}

Page 14: C#   classes

Outputin abcin abc Exception System.Threading.ThreadAbortException: Thread was being aborted. at yyy.abc()in abc Finally

Aborting a Thread while it is busy executing some code generates an exception. Thus, the function abc will be sent an exception, resulting in a call to the 'catch' and 'finally' clauses. The thread will be terminated after executing the code in the Catch and the Finally clause.

a.csusing System.Threading;public class yyy{public void abc(){try{System.Console.WriteLine("in abc");for ( ; ; ) ;}catch ( System.Exception e){System.Console.WriteLine("in abc Exception " +e.ToString() );}finally{System.Console.WriteLine("in abc Finally");}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Abort();t.Start();}}

Output(no output)

Aborting a thread before it has started execution does not generate any exceptions, since the thread is in a dead state. Hence, no code in abc ever gets called. This situation is akin to the one where you don your best attire to watch cricket live at the stadium, but the show gets ruined due to heavy downpour, resulting in the match being abandoned without a ball being bowled !

a.csusing System.Threading;public class yyy{public void abc(){for (int i = 1 ; i<= 10 ; i++ ) {System.Console.Write(i + " " );Thread.Sleep(1);}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();Thread.Sleep(10);t.Suspend();System.Console.WriteLine("\n After Suspend");

Page 15: C#   classes

Thread.Sleep(10);System.Console.WriteLine("Before Resume");t.Resume();}}

Output1 After SuspendBefore Resume2 3 4 5 6 7 8 9 10

We are allowed to Suspend a thread executing its code, at any point in time. In the above program, we let the thread run for a little while and then Suspend it. Thereafter, we Resume it. Thus, the thread is like a puppet in our hands. We can decide when and which strings to pull and the thread does our biding. If the thread has already been suspended, then Suspending it once again has no effect, and thus, it does not result in an error. A single Resume is enough to undo a million Suspends.

a.csusing System.Threading;public class yyy{public void abc(){for ( int i = 0; i<=3;i++){Thread t2 = Thread.CurrentThread;System.Console.Write(i + " " + t2.GetHashCode() + " ");Thread.Sleep(1);}}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();t1.Start();}}

Output0 2 0 3 1 2 1 3 2 2 2 3 3 2 3 3

Every thread is assigned a number internally. This number is called the HashCode. Hashing is the process of assigning a number to an entity so that it can easily be located from a long list. This hash number is useful and is pressed into service when we are required to keep track of a large number of threads internally.

a.csusing System.Threading;public class yyy{public void abc(){Monitor.Enter(this);for ( int i = 1; i<=5;i++){System.Console.Write(i + " " + Thread.CurrentThread.GetHashCode() + " ");Thread.Sleep(1000);}Monitor.Exit(this);}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();

Page 16: C#   classes

t1.Start();}}

Output1 2 2 2 3 2 4 2 5 2 1 3 2 3 3 3 4 3 5 3

Let us now clarify the abstract concept of the life of a thread with the help of an example. Any resource that many people desire is called a shared resource. Imagine the chaos that would be caused if five programs tried to simultaneously use a single network printer for printing, during their respectively time-slices. This would result in the printer printing a few pages for program 1, then a few pages for program 2, and so on. The printer will keep printing merrily, but the printed output will all be jumbled up. Thus, ideally, one program should use a shared resource at a time, till its job is complete. For this, a method has to be implemented by means of which, unless one program finishes executing some specific code in a function, no other program should be allowed to execute it. Thus, all other programs would have to wait until the first thread finishes execution. Let us assume that function abc contains code which sends a request to the printer to print. The thread t gets the first shot at running abc. The second thread t1 has to wait until thread t finishes execution, even if the duration of executing abc takes more time than one time slice, due to a large value being assigned as a parameter to the sleep function. This is achieved by using the static function Enter of the class Monitor. The function Enter is given an object which refers to the class in which the function resides. From that point onwards, every thread has to wait at Enter. The barrier lowers only when Exit is called from the Monitor class. Enter acts as a fussy security guard who decides as to which thread is to be prohibited and which one is to be permitted to execute code. No two threads can ever enter the Enter function together. It is a rigid one way lane from Enter to Exit. One car is allowed in at one time and another car can pass through only after the first car leaves Exit. You can see that the names Enter and Exit have been chosen very aptly.

a.csusing System;using System.Threading;public class ggg{};public class yyy{public ggg g1;public void abc( ){Monitor.Enter(g1);for ( int i = 1; i <= 3; i++){Console.Write(" Hell " + i);Monitor.Wait(g1);Console.Write(" Waitabc " + i);Monitor.Pulse(g1);}Monitor.Exit(g1);}};public class xxx{public ggg g1;public void pqr( ){Monitor.Enter(g1);for ( int i = 1; i <= 3; i++){Console.WriteLine(" Everyone " + i);Monitor.Pulse(g1);Monitor.Wait(g1);Console.Write(" Waitpqr " + i);}Monitor.Exit(g1);}};public class zzz{public static void Main(String[] args){ggg g = new ggg( );yyy a = new yyy( );

Page 17: C#   classes

a.g1 = g;Thread t = new Thread(new ThreadStart(a.abc));t.Start( );xxx b = new xxx();b.g1 = g;Thread t1 = new Thread(new ThreadStart(b.pqr));t1.Start( );}}; OutputHell 1 Everyone 1 Waitabc 1 Hell 2 Waitpqr 1 Everyone 2 Waitabc 2 Hell 3 Waitpqr 2 Everyone 3cs Waitabc 3 Waitpqr 3

The above example may be a protracted one, yet it is fascinating.The world has never been able to work with each other. The only global body we have is the UNO. People are mostly loners. In the entry point function Main, we create an object g, which is an instance of a class ggg, having no code or variable. It embodies the emptiness of space. This is a class that is incapable of doing any good or bad. We then create two objects that look like yyy and xxx respectively, and pass them as delegates, so that the threads can call functions abc and pqr. We also initialize the object g1, an instance of ggg, in each of them to g. This is crucial. The g1's in yyy and xxx represent the same g that is created in Main. After initializing the requisite members, we activate the two threads. As earlier, let us assume that the function abc gets called first. Monitor.Enter considers taking a handle to any object, which is to be synchronized between multiple instances of threads, as its first parameter. This function needs an Exit function to release its handle. Thus, the number of Exits in the program must correspond to the number of Enters. It prints "Hell", and then due to the Wait function, it waits indefinitely, till it finally receives runtime notification. However, the thread accomplishes nothing significant. Thus, we do not see the Waitabc displayed, since it is entered after the Wait function. The second thread starts executing the function pqr. Monitor.Enter uses the same synchronized object, and now it displays Everyone. The next function to be called in pqr is Monitor.Pulse. Pulse does a very simple thing. It wakes up any thread having the same handle, which happens to be waiting at a Wait. Thus, the thread in abc wakes up. However, the one in pqr waits for someone to Pulse it. By using Wait and Pulse, we can easily play a game of tag. The thread t performs some work and waits for thread t1 to wrap up whatever work it is preoccupied with. Thread t does not know how long thread t1 will take to fulfill its side of the bargain. But no sweat! Thread t waits at a Wait for the thread t1 to Pulse it. Then, thread t1 waits at a Wait for thread t to do some work. When thread t concludes, it Pulses thread t1, and so on. This can go on and may involve some other threads too. The threads use the handle g, which is the synchronized region. It is akin to being an integral part of a clandestine social group. The parameter to these Monitor functions clubs them together as a group. If you change the handle, the group breaks up, leaving your thread in a lurch. The Wait function can also be made to wait for a specified time period. If we change the line Monitor.Wait(g1) to Monitor.Wait(g1,0), the output changes dramatically. A monitor is associated with an object on demand and it cannot be instantiated at all. If the constructor is private, we can never create an object. But, the static functions of that object can be used. The Monitor functions can be called from anywhere since they are unbound. There are no restrictions on the sources of the object to be called.

a.cs using System.Threading;public class yyy {public void abc(){Monitor.Enter(this);Monitor.Enter(this);for ( int i = 1; i<=3;i++){System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");}Monitor.Exit(this);}}public class zzz{

Page 18: C#   classes

public static void Main() {yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();t1.Start();}}

Output1 2 2 2 3 2

The Exit function can be called as often as one desires. In this program, we have two threads, t and t1. Let us assume that thread t has the first go. At this time, it will pass through the first Monitor.Enter, as well as the second Monitor.Enter, without waiting at all. However, thread t1 will have to wait at the first Monitor.Enter, since the thread t called the Enter function twice, but Exited only once. Thus, the program never quits out. Had it called Exit twice, the thread t1 too would have woken up.

a.cs using System.Threading;public class yyy {public int j=1;public void abc(){Monitor.Enter(j);for ( int i = 1; i<=3;i++){System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");}Monitor.Exit(j);}}public class zzz{public static void Main() {yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));t.Start();}}

Output1 2 2 2 3 2Unhandled Exception: System.Threading.SynchronizationLockException: Exception of type System.Threading.SynchronizationLockException was thrown. at yyy.abc()

Monitor.Enter takes an object, and not a variable, as a parameter. This is the reason why we used this parameter or some reference object, in the earlier programs. If a variable or a value object is used, an exception will be thrown. Thus, value types or null objects are not allowed as parameters to Monitor.Enter.

a.csusing System.Threading;public class xxx{}public class ttt{}public class yyy{static int p = 1;xxx x = new xxx();ttt t = new ttt();public void abc(){if ( p == 1){Monitor.Enter(x);

Page 19: C#   classes

p++;}elseMonitor.Enter(t);for ( int i = 1; i<=3;i++){Thread.Sleep(1000);System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");}Monitor.Exit(x);}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();t1.Start();}}

Output1 2 1 3 2 2 2 3 3 2 3 3

The Enter function must be used with some caution. The variable p initially has a value of 1. The first thread t sees the value of p as 1. Hence, the 'if' statement is true. Thus, it crosses Monitor.Enter with x as a parameter. It then takes a snooze at the Sleep function, while the second thread t1 executes function abc, where it sees the value of variable p as 2. This thread now meets the Monitor.Enter function, with t as the parameter. The object reference given to this Enter is entirely at variance with the object given to the earlier one. Thus, both the threads execute the Enter function, which fails the very objective of the Enter function. For Enter to work as expected, it should be provided with the same object as a parameter to itself. The concept of a Lock is normally used to explain the Monitor class. One thread gets a Lock, while the others wait until the lock is released.

a.csusing System.Threading;public class yyy{public void abc(){bool b = Monitor.TryEnter(this);System.Console.WriteLine(b);for ( int i = 1; i<=3;i++){Thread.Sleep(1000);System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");}Monitor.Exit(this);}}public class zzz{public static void Main() {yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();t1.Start();}}

OutputTrueFalse1 2

Page 20: C#   classes

1 3 2 2 2 3 3 2 3 3

The TryEnter function is similar to the Enter function, but it does not block. If the thread enters successfully, TryEnter returns a true. This is what happens the first time. But in the case of the second thread t, it returns a false, even when it enters the critical section.

a.cs using System.Threading;public class yyy{public void abc(){bool b = Monitor.TryEnter(this,1000);System.Console.WriteLine(b);for ( int i = 1; i<=3;i++){Thread.Sleep(1000);System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");}Monitor.Exit(this);}}public class zzz{public static void Main(){yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Start();t1.Start();}}

OutputTrueFalse1 2 1 3 2 2 2 3 3 2 3 3

The TryEnter function is overloaded to accept a number as a parameter, which represents the time duration for which the TryEnter function should block or wait. In our case, the timeout exceeds the time that thread t spends in the function. Hence, we get a value of False. If we add an extra zero to the time parameter, it will return a true instead. If the value of the parameter is infinite, the behaviour of the TryEnter becomes akin to that of Enter. Thread Attributes

a.csusing System.Threading;public class yyy{[System.ThreadStaticAttribute ()]public static int j = 1;public static int k = 1;public void abc(){for ( int i = 1; i<=3;i++){Thread t2 = Thread.CurrentThread;j++;k++;System.Console.Write(i + "." + t2.Name + " j=" + j + " k=" + k + " ");Thread.Sleep(1);}}}

Page 21: C#   classes

public class zzz{public static void Main() {yyy a = new yyy();Thread t = new Thread(new ThreadStart(a.abc));Thread t1 = new Thread(new ThreadStart(a.abc));t.Name="One";t1.Name="Two";t.Start();t1.Start();}}Output1.One j=1 k=2 1.Two j=1 k=3 2.One j=2 k=4 2.Two j=2 k=5 3.One j=3 k=6 3.Two j=3 k=7

A static variable is a loner, as reiterated by us on numerous occasions. It belongs to the class and not to an object. Thus, there will always be a single static variable. Let us consider a case where we have two threads t and t1 executing code in the same function abc, that also contains static variables j and k. The first question that strikes us is 'Will each of the threads see the same static variable or a different one?' Also, if we assume that both threads cater to the same static variable, when one thread increments the variable, will the other thread see the new value? And if not, then, is the compiler creating a separate static variable for each thread? Too many questions that need answers! We want to possess the best of both the worlds. At times, we need the threads to work on the same static variable, while at other times, we need the threads to access separate copies of the static variable. The default behaviour is that of the static variable being shared. Thus, the variable k is incremented by both the threads, and each thread sees the same value. The value of k finally reaches 7 at the end. The variable j has an attribute ThreadStaticAttribute above it. This attribute may have a fancy name, but it merely creates a separate variable for each thread executing the function. Thus, we have two static variables j, one for each thread t and t1. Any changes made to the value of the variable j by t, does not get reflected in the static variable j of the thread t1. 1.One j=2 k=2 1.Two j=3 k=3 2.Two j=4 k=4 2.One j=5 k=5 3.Two j=6 k=6 3.One j=7 k=7 The above attribute only acts on static variables. We see the above output when we remove static from the variables j and k. The compiler is very clear. The attribute ThreadStaticAttribute is only to be used on static variables, but when applied on non-static variables, the compiler does not generate any error or warning. It simply ignores the attribute. Thus, instance variables are shared across threads, whereas, local variables like i are independent of threads. Thus, a variable can either be shared across threads or may be independent of the thread. No in-between options are allowed. Events

a.csusing System.Threading;public class zzz{public static void Main() {ManualResetEvent a;a = new ManualResetEvent(false) ;System.Console.WriteLine("Before WaitOne " );bool b = a.WaitOne(1000,false);System.Console.WriteLine("After WaitOne " + b);}}

OutputBefore WaitOne After WaitOne False

We have created an object a that is an instance of a ManualResetEvent class. The constructor is assigned a value of false. Then, we call a function WaitOne from this class with a number as the first parameter, and a boolean value as the second parameter. This number, 1000, is the number of milliseconds for which we want the thread to wait at the function WaitOne. The thread therefore waits for one second before quitting out, and then returns a False. Had we used the enum Timeout.Infinite that has a value of -1, we could have made the function wait forever. Thus, we can keep a thread waiting either for a specified duration of time or forever.

a.csusing System.Threading;public class zzz{public static void Main()

Page 22: C#   classes

{ManualResetEvent a;a = new ManualResetEvent(true) ;System.Console.WriteLine("Before WaitOne " );bool b = a.WaitOne(1000,false);System.Console.WriteLine("After WaitOne " + b);}}

OutputBefore WaitOne After WaitOne True

By changing the value in the constructor to true, the thread just refuses to wait for anyone. It just whizzes past, without even acknowledging the presence of the function WaitOne. Further, the return value of WaitOne i.e. b returns a True. Let us delve a little deeper into this mystery. A ManualResetEvent is like a boolean variable. It can possess only one of the two values, true or false. When we first created such an object, we gave it a value of false. So, the function WaitOne waited till the Event object turned into True or the time value expired. Since the time duration got over while waiting, and the value of the Event object was not set to True, it stopped blocking and returned with a value of false. In the next example, the event object already has a value true and hence, there is no wait. Here, the function WaitOne returns true because the non-blocking in this case, cannot be attributed to a timeout. Thus, a ManualResetEvent has two states, true or false; or in technical lingo, signaled or non-signaled. A value of true implies a signaled state, while a false would mean that it is in the non-signaled state.

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a;a = new ManualResetEvent(true) ;System.Console.WriteLine("Before WaitOne " );bool b = a.WaitOne(1000,true);System.Console.WriteLine("After WaitOne " + b);b = a.WaitOne(10000,true);System.Console.WriteLine("After second WaitOne " + b);}}

OutputBefore WaitOne After WaitOne TrueAfter second WaitOne True

Since we do not consider ourselves to be very cerebral, we shall desist from using fancy terminology like signaled or non-signaled. Instead, we shall stick to the simple true and false. We initially created the object a to be true or signaled (Oops! There we go again!). It then passed the first and the second WaitOne without waiting. This occurred because it is a Manual ResetEvent (as the name itself suggests), and not automatic. Once it is set to true, the event cannot change to false automatically. This has to be accomplished manually.

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a;a = new ManualResetEvent(true) ;System.Console.WriteLine("Before WaitOne " );bool b = a.WaitOne(1000,true);System.Console.WriteLine("After WaitOne " + b);a.Reset();b = a.WaitOne(10000,true);System.Console.WriteLine("After second WaitOne " + b);}}OutputBefore WaitOne After WaitOne TrueAfter second WaitOne False

Page 23: C#   classes

The Reset function can be used to change the state from true to false as it changes the state to false. Hence, it has to wait in queue like a commoner, since the event ignores the earlier WaitOne. After some time, it gets activated due to a timeout and the function WaitOne returns a False.

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a;a = new ManualResetEvent(false) ;System.Console.WriteLine("Before WaitOne " );a.Set();bool b = a.WaitOne(1000,true);System.Console.WriteLine("After WaitOne " + b);b = a.WaitOne(10000,true);System.Console.WriteLine("After second WaitOne " + b);}} OutputBefore WaitOne After WaitOne TrueAfter second WaitOne True

Now, despite the fact that that the Event is originally false or non-signaled, the Set function sets it to true or the signaled state. At this stage, no power on earth is equipped to stop the event as it surges past the WaitOnes, treating them with disdain. It is not in the least concerned about stopping-by to pay homage to them. Such arrogance ! The class ManualResetEvent is a sealed class, like most other classes belonging to the Thread family. The developers at Microsoft were of the belief that no one would be able to add any more value to the class, and thus forbade any changes. This class is also derived from another class called WaitHandle, which in fact, embodies functions like WaitOne etc. The constructor has to be passed a parameter that affirms the initial state of the Event object. No default state is available. The Set and Reset methods return a boolean value, which indicates whether the change has taken place successfully or otherwise. WaitHandle is a class from which ManualResetEvent is derived. A class that offers functions which are polite, is called a synchronization class. People who are polite, wait for others to finish doing their work, and it is only then that they proceed with their own work. Any class that waits for another is called a synchronization class. All synchronization objects derive from WaitHandle, and so does ManualResetEvent. We are cautioned not to use this class directly, since it is the other classes that use this class. The documentation very clearly specifies that the CLR i.e. the Common Language Runtime or code written by Microsoft, calls the constructor of WaitHandle. Therefore, do not use this class directly. You are being forewarned!

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a,b;a = new ManualResetEvent(false) ;b = new ManualResetEvent(true) ;WaitHandle [] w = new WaitHandle[2];w[0] = a; w[1] = b; System.Console.WriteLine("Before WaitAll " );bool c = WaitHandle.WaitAll(w,10000,true);System.Console.WriteLine("After WaitAll " + c);}}OutputBefore WaitAll After WaitAll False

We can complicate life as much as we like. Similarly, we can complicate the WaitOne, by introducing its elder brother, WaitAll. This function will wait for all the event objects to become true or signaled, or it will stay there till the timeout occurs. In this manner, life can be made as complicated as we like. Until all the events reach the signaled state, the WaitAll remains extremely stubborn and does not budge from its vantage position. It does not take cognizance if a few of the events are signaled. All the events should be signaled. This could be a number approaching infinity.

Page 24: C#   classes

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a,b;a = new ManualResetEvent(false) ;b = new ManualResetEvent(true) ;WaitHandle [] w = new WaitHandle[2];w[0] = a; w[1] = b; System.Console.WriteLine("Before WaitAll " );bool c = WaitHandle.WaitAll(w);System.Console.WriteLine("After WaitAll " + c);}}

OutputBefore WaitAll

The number of functions in the class WaitHandle may be numerous, but most of them are only helper functions, which make life tranquil. The WaitAll function, if called with a single parameter, assumes that the second parameter is Timeout.Infinite or -1. These overloads can be ignored safely. It is the same for the WaitOne method.

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a,b;a = new ManualResetEvent(false) ;b = new ManualResetEvent(true) ;WaitHandle [] w = new WaitHandle[2];w[0] = a; w[1] = a; System.Console.WriteLine("Before WaitAll " );bool c = WaitHandle.WaitAll(w);System.Console.WriteLine("After WaitAll " + c);}}

OutputBefore WaitAll Unhandled Exception: System.DuplicateWaitObjectException: Exception of type System.DuplicateWaitObjectException was thrown. at System.Threading.WaitHandle.WaitMultiple(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext, Boolean WaitAll) at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext) at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles) at zzz.Main()

The CLR (Common Language Runtime, if you have forgotten) never sleeps, but the compiler can be caught dozing at times. C# hates duplicates, and thus, we cannot pass the same handle to the WaitHandle array. If you attempt to do so, a runtime exception will be thrown even when there was no reason to throw an exception. The runtime could have simply ignored the duplicate, but it chose not to.

a.csusing System.Threading;public class zzz{public static void Main(){ManualResetEvent a,b;a = new ManualResetEvent(false) ;b = new ManualResetEvent(true) ;WaitHandle [] w = new WaitHandle[2];w[0] = b; w[1] = a; System.Console.WriteLine("Before WaitAll " );int c = WaitHandle.WaitAny(w);System.Console.WriteLine("After WaitAll " + c);

Page 25: C#   classes

}}

OutputBefore WaitAll After WaitAll 0

Change line containing w[0] = b; w[1] = a; to w[0] = a; w[1] = b; and the output changes to the following:-

Output Before WaitAll After WaitAll 1

The WaitAny function waits till any of the events change to signaled or true. When this occurs, it returns the array index of the event which caused the exit. In this case, object b is in the signaled state, and since it is the first member of the array, the return value is 0. On interchanging the object to b, which is the second array member, the return value becomes 1. If both events are in the signaled state, then the lower array index is returned. If a timeout occurs, the value returned is 258. AutoResetEvent Class

a.csusing System.Threading;public class zzz{public static void Main(){AutoResetEvent a;a = new AutoResetEvent(false) ;System.Console.WriteLine("Before WaitAll " );bool c = a.WaitOne(1000,true);System.Console.WriteLine("After WaitOne " + c);}}

OutputBefore WaitAll After WaitOne False

Let us now move up the value chain. The AutoResetEvent class works in the same way like a ManualResetEvent class in the above example. It simply waits for the timeout to take place or the Event to be signaled.

a.csusing System.Threading;public class zzz{public static void Main() {AutoResetEvent a;a = new AutoResetEvent(true) ;System.Console.WriteLine("Before WaitAll " );bool c = a.WaitOne(1000,true);System.Console.WriteLine("After WaitOne " + c);c = a.WaitOne(10000,true);System.Console.WriteLine("After WaitOne1 " + c);}}

OutputBefore WaitAll After WaitOne TrueAfter WaitOne1 False

Ah! Again the wait. The AutoResetEvent object safely crosses the first WaitOne. But there is one major variation now in that, the state of the object changes from true to false or signaled to non-signaled. Thus, it is obliged to pay homage for some time at the second Wait. The name here is Auto and not Manual. Just as we would change color if we saw a wild boar charging at us, the AutoResetEvent changes state at the Wait. Everything else remains identical. Mutexes

a.csusing System.Threading;public class zzz{static Mutex m;

Page 26: C#   classes

public static void Main(){m = new Mutex(true,"vijay");zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();}public void abc( ){System.Console.WriteLine("abc start");m.WaitOne( );System.Console.WriteLine("abc end");}}

Outputabc start

If the boolean value of true in the Mutex constructor is changed to false, the output will change to the following:-

Outputabc startabc end

A mutex is a synchronization object derived from the class WaitHandle. A Mutex is, in some sense, similar to the other Event classes. We commence by creating a mutex and assigning it a boolean value of either true or false. In this context, these values have a slightly different connotation. They decide whether the creating thread owns the object or not. True allows the thread to own it, whereas False does not. Thus, with a mutex, we need to become very materialistic and start taking ownership of things. The current thread owns the mutex while the other threads are inclined to own it. True or false, signaled or non-signaled, own it or not, are all the same concepts. The main thread already owns the mutex, which is named as vijay. The name is optional and is used mostly to refer to the mutex. Thereafter, we create a thread which calls the function abc. After displaying a message, the function WaitOne is called. This function will now stake a claim for ownership of the mutex, but since the main thread owns it, thread t cannot proceed beyond this function until the original thread gives up ownership. If we change the True in the constructor to False, then the ownership is not claimed. Thus, the thread t will not have to wait at the WaitOne as there is no owner.

a.cs using System.Threading;public class zzz{static Mutex m;public static void Main(){m = new Mutex(true,"vijay");zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();}public void abc( ){System.Console.WriteLine("abc start");m.ReleaseMutex();m.WaitOne( );System.Console.WriteLine("abc end");}}Outputabc start Unhandled Exception: System.ApplicationException: Attempt to release mutex not owned by caller. at System.Threading.Mutex.ReleaseMutexNative(IntPtr handle) at System.Threading.Mutex.ReleaseMutex() at zzz.abc()

The main thread becomes the owner of the mutex as the boolean value supplied in the mutex constructor is True. Then, in the function abc, we try to release the ownership of the mutex by using the function ReleaseMutex. The thread t does not own the mutex. Hence, we get a runtime error as only the owner i.e. the main thread, can release the ownership of a mutex and pass it on to another thread.

a.cs using System.Threading;

Page 27: C#   classes

public class zzz{static Mutex m;public static void Main() {m = new Mutex(true,"vijay");zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();System.Console.WriteLine("Before Thread Sleep");Thread.Sleep(10000);System.Console.WriteLine("After Thread Sleep");m.ReleaseMutex();System.Console.WriteLine("All over");}public void abc( ){System.Console.WriteLine("abc start");m.WaitOne( );System.Console.WriteLine("abc end");}}

OutputBefore Thread Sleepabc startAfter Thread Sleepabc endAll over

Now alls well that ends well. The main thread sleeps for a little while and then calls ReleaseMutex. This awakens the thread t, and thus, the function abc now quits out. Remember that you cannot give away what you do not own.

a.csusing System.Threading;public class zzz{static Mutex m;public static void Main(){m = new Mutex(true,"vijay");zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();System.Console.WriteLine("Before Thread Sleep");Thread.Sleep(1000);System.Console.WriteLine("After Thread Sleep");m.ReleaseMutex();System.Console.WriteLine("Before WaitOne");m.WaitOne();System.Console.WriteLine("All over");}public void abc( ){System.Console.WriteLine("abc start");m.WaitOne();System.Console.WriteLine("abc before sleep");Thread.Sleep(100000);System.Console.WriteLine("abc end");}}

OutputBefore Thread Sleepabc startAfter Thread Sleepabc before sleepBefore WaitOne

There is a long sleep in the function abc. The mutex is still owned by the thread t, while the main thread is trying to regain its ownership. It can only gain ownership if the thread t releases the mutex or dies. Thus, until the thread t dies, we will not see

Page 28: C#   classes

All Over displayed at all. We could have had another sleep in abc and we could have released the mutex before that sleep. The choice is ours. Thus, a mutex is fully concerned with ownership issues. Either you own a mutex or you do not.

a.csusing System.Threading;public class zzz{static Mutex m;static Mutex n;public static void Main(){m = new Mutex(true,"vijay");n = new Mutex(true);zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();System.Console.WriteLine("All over");}public void abc( ){System.Console.WriteLine("abc start");Mutex [] g = new Mutex[2];g[0] = m;g[1] = n;Mutex.WaitAll(g);System.Console.WriteLine("abc end");}}

OutputAll overabc start

The program will hang. This is because we have two mutexses that have to be waited upon. Both m and n fill up the array g, and since both are true, the function abc will wait forever. Same rules of event classes apply to a mutex object.

a.cs using System.Threading;public class zzz{static Mutex m;public static void Main(){m = new Mutex(true,"vijay");zzz a=new zzz( );Thread t= new Thread(new ThreadStart(a.abc));t.Start();System.Console.WriteLine("Before Thread Sleep");Thread.Sleep(1000);System.Console.WriteLine("After Thread Sleep");m.ReleaseMutex();System.Console.WriteLine("Before WaitOne");m.WaitOne();System.Console.WriteLine("All over");}public void abc( ){System.Console.WriteLine("abc start");m.WaitOne();System.Console.WriteLine("abc after Wait");m.WaitOne();System.Console.WriteLine("abc after second Wait");Thread.Sleep(10000);System.Console.WriteLine("End");}}

Output Before Thread Sleepabc startAfter Thread Sleepabc after Waitabc after second WaitBefore WaitOneEnd

Page 29: C#   classes

All over The same rules about mutex ownership as mentioned earlier are discussed here. There may be two WaitOne functions, but the first WaitOne that gets the ownership, retains it. Thus, there was no wait between the first and the second wait. The concept of a mutex is extremely simple. A particular thread owns it. It can cross a wait without waiting and only when it releases the mutex can another thread claim and possess its ownership. Other threads cannot own it and they will have to wait at a wait. Dual ownership is not allowed.

a.csusing System.Threading;public class zzz{static Mutex m;public static void Main(){m = new Mutex(false,"vijay");zzz a=new zzz( );System.Console.WriteLine("abc start");m.WaitOne( );System.Console.WriteLine("abc end");}}

Output abc startabc end

A Mutex only works with multiple threads. So, using a mutex with a single thread is futile, because a mutex is concerned with determining as to which thread owns it. Therefore, there must be at least two threads. Timers

a.csusing System.Threading;public class zzz{public static void Main() {zzz a = new zzz();a.pqr();}public void pqr(){Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100,0);Thread.Sleep(1000);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

Outputhell

Timers are time-related. We have created a timer object and have passed it 4 values in its constructor. The first parameter is the delegate object which represents the function that should be called after a certain interval elapses. The second parameter is the object to be passed to the timer function. The third and fourth parameters are numbers representing time i.e. time and period. If the thread is not asked to sleep, the main thread will quit out and there will be no thread running. The entire program will halt and all the unfired timers will die. Thus, if we remove the last Sleep, the word 'hell' does not get displayed. The timer delegate has to receive an object as a parameter since it is part of the delegate signature. The timer also has to wait for a specified time which is stated in the constructor, before it can call the function. This wait is accomplished using a thread in a thread pool. Needless to say, the Timer class is a sealed class.

a.csusing System.Threading;public class zzz

Page 30: C#   classes

{public static void Main(){zzz a = new zzz();a.pqr();}public void pqr(){Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100,0);t.Dispose();Thread.Sleep(1000);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

Output(none)

If we ever desire to cancel the timer for whatever reasons, we can call the Dispose method to kill the timer. The timer dies and the function to be called does not get called at all.

a.csusing System.Threading;public class zzz{public static void Main(){zzz a = new zzz();a.pqr();}public void pqr(){Timer t;ManualResetEvent e = new ManualResetEvent(false); yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,10000,300);t.Dispose(e);e.WaitOne();System.Console.WriteLine("All Over");}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

OutputAll Over

The Dispose function can also take a parameter which is an event that gets signaled when the timer dies. Thus, the program that would have waited indefinitely now quits out because, removing the timer from the timer queue instantly signals an event.

a.csusing System.Threading;public class zzz{public static void Main(){zzz a = new zzz();a.pqr();}

Page 31: C#   classes

public void pqr(){Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100,1);Thread.Sleep(500);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

Output hellhellhellhelland so on

The third parameter to the timer constructor is the duration in milliseconds after which the timer should be fired. The second number can be either a 0 or any other number. Zero means the timer fires once and any other number signifies it as a periodic timer. This periodic timer keeps executing the function abc every 100 milliseconds, until told to do otherwise.

a.csusing System.Threading;public class zzz{public static void Main(){zzz a = new zzz();a.pqr();}public void pqr(){Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100,1);Thread.Sleep(115);t.Change(100,0);Thread.Sleep(1300);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

Outputhellhellhell

The Change function is used to stop a periodic timer. Thus, the timer can be made to run for some time in a periodic manner and thereafter, the Change can be used to stop it from executing repeatedly.

a.csusing System.Threading;public class zzz{public static void Main(){zzz a = new zzz();a.pqr();}public void pqr(){

Page 32: C#   classes

Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100000,1);Thread.Sleep(115);t.Change(100,0);Thread.Sleep(1300);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}

Outputhell

The timer initially is set to go off after a very long time. We let the main thread sleep for a little while but then change the due time of the timer to a smaller value. Thus, we can use the Change function to alter the initial settings of the timer.

a.csusing System.Threading;public class zzz{public static void Main(){zzz a = new zzz();a.pqr();}public void pqr(){Timer t;yyy b = new yyy();t = new Timer(new TimerCallback(b.abc),this,100,300);Thread.Sleep(1000);}}public class yyy{public void abc(object o){System.Console.WriteLine("hell");}}Outputhellhellhell

Our dictum has always been to understand things one step at a time. The last parameter to the timer constructor can either be zero or any other value. If the value is 100, it means that every hundred seconds the timer will go off, until it is stopped by a Change or a Dispose. Earlier, we had used the number 1 to signify that the timer should be called every 1 second. You are free to decide these timer values depending upon what you intend to use the timer for. The values cannot be negative and cannot exceed 4294967294 either. If you do so, an exception will occur. Exceptions, lest you have forgotten, are generated at runtime only. Thread Pool

a.csusing System;using System.Threading;public class yyy {public ManualResetEvent a = new ManualResetEvent(false);public void abc(Object s){a.WaitOne(10,true);Console.WriteLine("{0} ", Thread.CurrentThread.GetHashCode());}};public class zzz {public static void Main() {

Page 33: C#   classes

ManualResetEvent b = new ManualResetEvent(false);yyy y = new yyy();for (int i=1;i<=5;i++){ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);}b.WaitOne(10000,true);}}Output 22222

The rationale of a thread pool is that the decision makers at Microsoft did not want us to spend time taking care of threads like mothers take care of babies. We will first explain the above program and then delve into the nitty-gritty of thread pools. Since we are lazy, we use a for statement to avoid repeating the same code over and over again. The function QueueUserWorkItem will be called at least five times because it is placed in this for loop. QueueUserWorkItem function requires one, or at the most, two parameters. The first parameter, which is the name of a function, is most crucial. The name is never ever assigned directly, but indirectly, using a WaitCallback object. The function to be called is y.abc. Hence, it is passed a parameter to WaitCallback. The second parameter to the function QueueUserWorkItem is the number 1. As a result, the function abc has to accept a parameter of type object which will contain the value 1. A parameter of type object does not have any useful code and by itself is totally useless. Every entity in C# is finally derived from the object class. Thus, every entity is of an object type. Whenever we are not aware of the parameter type to be received in a function, or in cases where we want to receive multiple data types, the data type of object is used. Object is like a rubber man; it can fit anywhere and everywhere. By giving the function name abc to the function QueueUserWorkItem, abc will be called five times by a thread. The function waits for a small amount of time at an event and then uses the function GetHashCode to print the name/id of the thread. This function will print the thread id that is calling abc. From our output, we can conclude that only two threads execute the function abc. Commenting the Wait statement will display the same id for the thread. This means that no new thread executes the function. Thus, instead of two threads, only a single thread is used. Efficiency, thy name is Thread Pool. We are thus posting work items to a common place or thread pool. Then, a thread is allocated to execute the work item or function. The first available thread from the thread pool executes the function. We are only using one thread pool per program. There is a lot of overhead in managing threads. A lot of time is spent figuring out which thread is sleeping, i.e. waiting for an event to occur. Most of the time, threads are in this inert state. At times, threads wake up to check/poll for a change that is expected to occur, such as a key press, or to update some counter or status information and fall asleep again. Threads are like bears, they love sleeping. Thread Pooling is a means of managing threads more efficiently. This is done by providing your application with a pool of threads that are managed by the system. All threads that do work are called worker threads. There is a thread that monitors the status of threads waiting at any of the wait functions. When the wait is over, the callback function will be executed. The next program demonstrates how a function can be called after a specified time duration has elapsed. There is no way to cancel a function from being executed once it has gone into the thread pool. A prayer will also not help. Timers and Waits use the thread pool without our knowledge. The thread pool is created when we call the function QueueUserWorkItem for the first time or when we use a timer function. The number of threads that form part of a thread pool is limited by the amount of memory available. By today's standards, this means that more threads can be made available than we would ever need. These threads receive the default stack size and run at the default priority. No special priority is accorded. For the trivia fans, a thread can handle up to 63 wait operations. No concept is perfect. Thus, one significant disadvantage of a thread pool is that it involves a long calculation.

a.csusing System;using System.Threading;public class yyy{public void abc(object s, bool b){Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),b);}};

Page 34: C#   classes

public class zzz{public static void Main(){ManualResetEvent a = new ManualResetEvent(false);ManualResetEvent b = new ManualResetEvent(false);yyy y = new yyy();ThreadPool.RegisterWaitForSingleObject(a,new WaitOrTimerCallback(y.abc),1,1000,true);b.WaitOne(10000,true);}}

Output2 True

As mentioned earlier, we would like our function to be called after a certain wait. To accomplish this, we use a function with a very large name called RegisterWaitForSingleObject. The first parameter is the handle of an event. This has been set to false or non signaled. The second parameter is a delegate of type WaitOrTimerCallback which is similar to a WaitCallback. The difference is that the signature of the function given to this function, which is abc, has to have two parameters, viz. an object and a boolean variable. The third parameter is the object that is to be passed to the function. The second last parameter is a timer that decides the duration after which the function should be called. Finally, the last parameter decides whether the function is to be executed only once or every time the timer expires. As our event object is set to false, the function gets called after 1000 milliseconds. When the timer expires, the function abc obtains a value of true. By merely changing one line in the above example, the event can be changed to a signaled state.

ManualResetEvent a = new ManualResetEvent(true);

Output 2 False

The function abc gets called immediately, unlike in the earlier program. Also, there is no wait as the event is true or signaled. The function abc also knows why it has been called since the bool parameter has a value of false, which was true in the earlier case. Thus, the function abc can look at the value of its second parameter, which indicates whether the timer has elapsed or the event has been signaled.

a.cs using System;using System.Threading;public class yyy{public void Beta(object s, bool b){Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),b);}};public class zzz{public static void Main(){ManualResetEvent a = new ManualResetEvent(false);ManualResetEvent b = new ManualResetEvent(false);yyy y = new yyy();ThreadPool.RegisterWaitForSingleObject(a,new WaitOrTimerCallback(y.Beta),1,10000,true);a.Set();b.WaitOne(10000,true);}}

Output2 False

In this program, initially we set the event to false and then, using the Set function, we set it to true immediately. Thus, the function gets called instantly. You would remember that the function gets called whenever the event is in a signaled state or the timer expires. This function first checks whether the wait object is signaled or not. If it is signaled, it executes the function. If it is not, it waits for the timer to elapse or the wait object to come into the signaled state, whichever occurs first. Interlocked

a.csusing System;

Page 35: C#   classes

using System.Threading;public class yyy{public ManualResetEvent a = new ManualResetEvent(false);int i = 10;public void abc(Object s){Interlocked.Increment(ref i);Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),i);}};public class zzz{public static void Main() {ManualResetEvent b = new ManualResetEvent(false);yyy y = new yyy();for (int i=1;i<=5;i++){ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);}b.WaitOne(10000,true);}}

Output2 112 122 132 142 15

Threads can be dangerous for the unwary. The function abc gets called five times by the same or a different thread. Consider a case where one thread may be incrementing the line i = i + 1. To do so, it will start by asking what is the value of i. Let us suppose that it is 10. It then increments the value by 1 to make it 11. It may so happen that just as it is about to read its value, its time slice may get over and another thread may increment the variable i by 1 to make its value 12. When the first thread receives its next time slice and checks the value of i, it will see the value as 12 and not 11. Thus, the variable would have been incremented twice instead of once. The Interlocked class has a sole purpose in life, i.e. to ensure that the variables are not incremented more than once. In a sense, it synchronizes access to a variable that is being shared by a number of threads. The operation will either be done in a single time slice or not done at all. It will not be carried over into another time slice ever. These operations, which are guaranteed to be finished in a single time slice, are called atomic operations.

a.csusing System;using System.Threading;public class yyy{public ManualResetEvent a = new ManualResetEvent(false);public int i = 10;public void abc(Object s){i++;Interlocked.CompareExchange(ref i,100,12);}};public class zzz{public static void Main(){ManualResetEvent b = new ManualResetEvent(false);yyy y = new yyy();for (int i=1;i<=5;i++){ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);}b.WaitOne(1000,true);Console.WriteLine(y.i);}}

Output

Page 36: C#   classes

103 The function abc gets called five times. If we intend to carry out comparisons, we would face the above dilemma. Thus, we use a simple function called CompareExchange. This functions checks the value of the first parameter that is passed as a reference, with the third parameter, which in our case is 12. If it matches the value, the first parameter i.e. i is assigned the value of the second parameter i.e. 100. All this is done in a single time slice. Thus, whenever variable i becomes 12, its value shoots up to 100, and as a result, the final answer becomes 103. Like the Increment, there is a Decrement function also that reduces the value of a variable by one. As an aside, a ref parameter cannot be replaced by an out parameter. In the same vein, the Exchange function changes the value of two variables passed to it as ref parameters. This is done in an atomic manner.

2 Internet Classes Before weaving our way into the Internet, let us investigate a few more fundamentals about the C# programming language, in order to acquire a superior appreciation of internet programming. No computer in the world is equipped to recognize the letters of the alphabet on its own. Therefore, an internal representation has to be used. One such representation is called ASCII, which allots a number falling between 0 to 255, to each of the letters. Here, each letter is represented by a byte, which is an 8-bit number. For instance, the letter 'A' is represented by the number 65, while the letter 'a' is represented by the number 97. However, languages such as Japanese and Chinese are remarkably visual, and require a larger range of numbers to represent them in totality. In order to accommodate the requirement of these languages, the computer industry came out with a standard called UNICODE, which can represent every known language in the world in its entirety. But, to accomplish this, this standard imposes the condition that every character in the language should be represented by a 16-bit number, and not by an 8-bit number.

a.cspublic class zzz{public static void Main(){byte i = 65;System.Console.WriteLine(i);System.Console.WriteLine((char)i);i = (byte)'A';System.Console.WriteLine(i);}}

Output65A65

The C# language takes cognizance of the fact that the byte data type represents a number ranging from 0 to 255. Thus, by default, the WriteLine function displays the number that the byte represents. If we typecast the byte i to a char, the WriteLine function displays its ASCII equivalent instead. The C# language had to indoctrinate a new data type called 'char', which could represent a UNICODE character or a number ranging from 0 to 65535, since a byte is too diminutive to be able to accommodate this range of values. A literal or char 'A' cannot be initialized to a byte, since we cannot equate a smaller 8-bit data type to a larger 16-bit data type. Therefore, a cast operator has to be used.

a.cspublic class zzz{public static void Main(){char i = 'A';System.Console.WriteLine(i);}}

Output

Page 37: C#   classes

A A variable of char data type cannot be equated to a number. It can only be initialized to a literal. The WriteLine function prefers to receive the 'char' data type, since it can display the value as a letter of the alphabet. Thus, the 'char' data type is used to store 16 bit UNICODE characters, while the 'byte' data type is employed to store small numbers. The 'string' data type is used for a sequence of UNICODE characters.

a.cspublic class zzz{public static void Main(){byte [] i = new byte[5];i[0] = 65; i[1] = 66;System.Console.WriteLine(i);char [] j = new char[5];j[0] = 'A'; j[1] = 'B';System.Console.WriteLine(j);}}

OutputSystem.Byte[]AB

Dealing with an array of bytes is not as straightforward and uncomplicated as dealing with an array of chars. In case of an array of bytes, the WriteLine function merely displays the data type of the array, whereas, in the case of an array of chars, it displays the entire array. Thus, we need a technique, by means of which, we are able to display the bytes present in the form of an array of bytes, using the WriteLine function.

a.cspublic class zzz{public static void Main(){byte [] i = new byte[5];i[0] = 65; i[1] = 66; i[2] = 67;i[3] = 68;string s = System.Text.Encoding.ASCII.GetString(i, 1, 2);System.Console.WriteLine(s);}}

OutputBC

The namespace System.Text.Encoding has a class called ASCII, which contains a static function GetString. This function accepts three parameters:

• The array of bytes, which is to be converted into a string.• The starting point in the array ( in our case, 1) refers to the second element in the Array, which is the letter 'B'. • The number of characters or the length of the string. We have specified it as 2. Hence, only 2 characters are seen.

The function returns a string, which is displayed by employing the WriteLine function. This methodology thereby, assists in converting a certain number of bytes from a byte array into a string, which can then be effortlessly printed by the WriteLine function. The rationale for introducing this function in this chapter is due to the existence of a large number of functions that return data as an array of bytes. The approach demonstrated above may be employed, to display such data in all such cases.

a.cspublic class zzz{public static void Main(){string s1 = "vmukhi"; byte[] i; char [] j = s1.ToCharArray();i = System.Text.Encoding.ASCII.GetBytes(j); string s = System.Text.Encoding.ASCII.GetString(i, 0, 5);System.Console.WriteLine(s);}}

Page 38: C#   classes

Outputvmukh

We now need to resolve another quandary. In the internet-related classes, functions read and write data in the form of an array of bytes. We are comfortable dealing with strings, as they are the most natural data type available for representing data. However, this data type has to be converted into an array of bytes, if it has to be transmitted. But, there is no direct mechanism of attaining this conversion. The only possibility is to write our own function. The string is first converted into an array of chars, using ToCharArray method in the string class. As of now, there exists no magic formula in the string class, which can directly provide an array of bytes. So, we are compelled to utilize the GetBytes function from the ASCII class, which accepts a character array and converts it into an array of bytes. This is an additional step that needs to be executed in the conversion process. The last two lines of the above program merely verify whether all data has been supplied correctly or otherwise.

a.cspublic class zzz{public static void Main(){byte [] a;a= abc("ABC");string s = System.Text.Encoding.ASCII.GetString(a, 0, 3);System.Console.WriteLine(s);}public static byte [] abc( string a){byte [] b = new byte[a.Length];System.Console.WriteLine(a.Length);for ( int i = 0 ; i < a.Length ; i++){System.Console.WriteLine(a[i]);b[i] = (byte)a[i];}return b;}}

Output3ABCABC

In this program, we write our own function that accepts a string as a parameter, and returns an array of bytes. This function eliminates the intermediate step of converting the string into an array of chars, prior to its final conversion into an array of bytes. The Length member in the string returns the length of the string, and also determines the size of the byte array, which is to be created. In the 'for' loop, every individual character can be accessed, one at a time, by using the indexer property of the string class. Concurrently, the byte array is also filled up. The need for a cast operator has been explained earlier. Finally, at the end of the function, we return this freshly filled up array, and thereafter, display the byte array. The array is displayed merely to corroborate that all actions have been executed as planned. File Handling

a.csusing System;using System.IO;class zzz{public static void Main(){FileStream f = new FileStream("c:\\z.txt", FileMode.Open, FileAccess.Read);byte [] a;a = new byte[512];int b = f.Read(a, 0, 512);Console.WriteLine(b);string s = System.Text.Encoding.ASCII.GetString(a, 0, b);Console.Write(s);

Page 39: C#   classes

} }z.txtvijaymukhi

Output12vijaymukhi

We start by creating an object f, which is an instance of class FileStream. The function is assigned the name of the file that we want to work with, along with the mode in which it is to be opened. The two parameters, FileMode.Open and FileAccess.Read, are enums, which open a file for reading. The Read function in the file stream reads the file. The function has 3 parameters:

• The first parameter is a byte array. The data from the file is to be read into this array.• The second parameter is the offset position.• The third parameter is the maximum number of bytes to be read.

Thus, the Read function shall attempt to read upto 512 bytes from the file, until it reaches the end of the file. Since the file contains only the words 'vijay mukhi', the output is 12, which represents the number of bytes read. This is followed by the data, i.e. vijay mukhi. Let us now browse through the data sent by a web server.

a.csusing System;using System.Net;using System.IO;class zzz{public static void Main(){WebRequest r = WebRequest.Create("http://www.vijaymukhi.com");WebResponse re = r.GetResponse();Stream s = re.GetResponseStream();Byte[] a = new Byte[51200];int b = s.Read(a, 0, 51200);Console.WriteLine(b);Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));} }

Output1085<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frame.dtd"><html><head><title>Vijay Mukhi's Technology cornucopia</title></head><frameset frameborder="0" framespacing="0" border="0" cols="*" rows="97,*"> <frame marginwidth="0" marginheight="0" src="heading.html" name="heading" noresize scrolling="no"> <frameset frameborder="0" framespacing="0" border="0" cols="210,*" rows="*"> <frameset frameborder="1" framespacing="0" border="0" cols="*" rows="0,*"> <frame marginwidth="0" marginheight="0" src="code.html" name="code" noresize scrolling="no" frameborder="0"> <frame marginwidth="5" marginheight="5" src="menu_empty.html" name="menu" noresize scrolling="auto" frameborder="0"> </frameset> <frame marginwidth="5" marginheight="5" src="main.html" name="text" noresize> </frameset><noframes><p>The <code>NOFRAMES</code> element is to be used to give useful content to people with browsers that cannot display frames. One example is Lynx, a text-based browser.</p></noframes></frameset></html>

We expect you to either have a web server installed on your machine, or at least be connected to the net. Every machine on the internet is recognized by a name. For example. the Microsoft site is known as www.microsoft.com; the Java site on the net is known as www.javasoft.com; and my site is known as www.vijaymukhi.com. However, every machine

Page 40: C#   classes

is also known by the common name of 'localhost'. When the address specified in the browser is www.microsoft.com, the web server of that machine sends across an HTML file to the browser. The file that is sent across is either named index.html or default.html. In the above program, we would like to simulate the functioning of a browser, i.e. we want to fetch an HTML file from a web server. In the System.Net namespace, there is a class called WebRequest that has a static function called Create. This function requires the name of the machine that runs a web server. While reading this book, in case you are not connected to the Internet, you may use 'http://localhost/' as the name of the machine. Or else, you may connect to a site on the net such as www.vijaymukhi.com. A WebRequest object r is returned by the Create function. The WebRequest class has another function called GetResponse, which returns a WebResponse object. The value returned is stored in a variable called re. Calling the method GetResponseStream from the WebResponse object returns a Stream object that can be utilized to read the HTML file sent across by the web server. The file handling concepts, learnt a short while ago, may be applied here too. The interaction between our program and the web server is handled by two classes, i.e. WebRequest and WebResponse. We have no inkling as to what each class accomplishes. In order to read the HTML file off the Stream object, we create a byte array having a size of 51200. Thereafter, we employ the Read function to read 51200 bytes, from the inception of the stream, into the array. The value returned by the Read function is indicative of the number of bytes that have been read off the stream. On displaying this value, the number obtained is less then 51200 (in our case it is 1085), and not 51200. Therefore, despite our having commanded the Read function to read upto 51200 bytes, it read only 1085 bytes. This is because it encountered an End Of File mark after having read 1085 bytes. In case of an error, the WebResponse object displays a Status code. Few of the values that may be assigned to this Status code, are as follows:

• 200 indicates 'success'.• 403 indicates 'permission denied'.• 404 indicates that the 'file name does not exist'.

Since no errors are visible in our case, we assume the status code to be 200. Thereafter, the byte array is converted into a string, so that it can be displayed using the GetString function. Therefore, we see the first 1085 bytes of the HTML file, sent by the web server.

a.csusing System;using System.Net;using System.IO;class zzz{public static void Main(){WebRequest r = WebRequest.Create("http://www.yahoo.com");WebResponse re = r.GetResponse();Stream s = re.GetResponseStream();Byte[] a = new Byte[51200];int b = s.Read(a, 0, 51200);while (b > 0){Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));b = s.Read(a, 0, 512);System.Console.WriteLine(b);}} }

We are not in a position to estimate the file size of the HTML file, which has been sent by the web server. So, we take recourse to the 'while' loop in situations of incertitude or perplexity. The Read function returns a zero, when there is no more data to be read from the stream. Thus, the 'while' loop terminates when the value of b becomes zero. Therefore, the last line of the output would always display a zero. In fact, we can regulate the number of bytes that we want the Read function to read off the stream. Here, we specify a value of 512. The framework enjoys the license to ignore this number. In order to retrieve a specific file off the web server, such as a.html, we add the name of the file at the end of the URL. If the file appertains to the local hard disk, the Create function will be written as follows:

WebRequest r = WebRequest.Create("http://localhost/a.html");

Page 41: C#   classes

The next query which is likely to vex our minds is: Where would the file a.html be stored on the hard disk? The answer to this depends upon the web server installed on the machine. In case of IIS or PWS from Microsoft, the file a.html would be placed in the sub-directory c:\inetpub\wwwroot. Reading a file sent by a web server is similar to reading a file from the local disk. Therefore, we do not have to discover two separate ways of reading the same thing.

a.csusing System;using System.Net;using System.IO;class zzz{public static void Main(){Uri u = new Uri("http://www.yahoo.com");HttpWebRequest wr = (HttpWebRequest) WebRequest.Create(u);AsyncCallback a = new AsyncCallback(abc);IAsyncResult r = (IAsyncResult) wr.BeginGetResponse(a,wr);Console.ReadLine();}static void abc(IAsyncResult ar){HttpWebRequest r = (HttpWebRequest) ar.AsyncState;HttpWebResponse re = (HttpWebResponse) r.EndGetResponse(ar);Stream s = re.GetResponseStream();Console.WriteLine("Status code: " + re.StatusCode);Byte[] a = new Byte[51200];int b = s.Read(a, 0, 51200);while (b > 0){Console.Write(System.Text.Encoding.ASCII.GetString(a, 0, b));b = s.Read(a, 0, 512);System.Console.WriteLine(b);}}}

The above program displays the same output as before, but with a difference. It follows a dissimilar method of writing code. In the previous example, the Read function waited till the data was received from the Internet. When we are connected to the Internet, this wait could be indefinite. This effectively would disable the program from proceeding any further. While awaiting data, the program cannot carry out any other activity. Thus, to avoid squandering of time and resources, we need a mechanism, by means of which, the program can be intimated about the receipt of data for the HTML file. The static function Create, now accepts a URI as a parameter. To create this URI object, we pass the same URI string as a parameter to the constructor. The return value of this function is stored in a HttpWebRequest object, since this class is derived from the class WebRequest. This class has a function BeginGetResponse, which accepts two parameters. The first is an AsyncCallback delegate, which requires the name of a function. In the C# world, a delegate simply stands-in for a function to be called. The second parameter is a State object. Despite our not making any use of any state related information, we are not allowed to supply a null value. It is mandatory to supply a value, without which, an error is generated. Thus, we specify the object wr, of type HttpWebRequest, as the parameter. Under normal circumstances, we are required to create a State object and furnish it as a parameter. After a certain time duration, data arrives from the web server, and the function abc gets called. This function is passed IAsyncResult as a parameter, which has a property AsyncState. Since this is an Object, we cast it to HttpWebRequest. We require an HttpWebResponse object, in order to obtain a Stream object, which will be employed to read the file received from the web server. The HttpWebRequest class has a EndGetResponse function, which accepts an IAsyncResult as a parameter, and returns a HttpWebResponse object. This object, in turn, is derived from the HttpResponse class. This function terminates the asynchronous request. The remaining code is similar to the above program. We however, are at liberty to execute other tasks, while the file is being received. Normally, a separate thread runs the function abc in an asynchronous manner.

a.csusing System;using System.Net;using System.IO;class zzz{public static void Main(){Uri u = new Uri("http://www.yahoo.com");

Page 42: C#   classes

HttpWebRequest w = (HttpWebRequest) WebRequest.Create(u);IAsyncResult r = (IAsyncResult) w.BeginGetResponse(new AsyncCallback(abc),w);Console.ReadLine();}static void abc(IAsyncResult ar){HttpWebRequest r = (HttpWebRequest) ar.AsyncState;HttpWebResponse re = (HttpWebResponse) r.EndGetResponse(ar);int b = 0;char[] a = new char[512];StreamReader rd = new StreamReader(re.GetResponseStream(), System.Text.Encoding.UTF8);StringWriter w = new StringWriter();b = rd.Read(a, 0, 512);while (b != 0 ){w.Write(a, 0, 512);b= rd.Read(a, 0, 512);}Console.WriteLine("Message = " + w.ToString());}}

There are a myriad ways of achieving the same output. In our case, we have presented a program that displays the same output as before, by using a different set of classes. The variations are only in the function abc. We first create an array of chars and not bytes. Thereafter, a StreamReader object rd, which is derived from TextReader, is created. This class has the ability to read characters from the Stream object supplied to it. The second parameter to the constructor is the encoding-type to be used, while reading character input from a stream. The Stream class is designed for byte input-output only. By default, it takes the UTF-8 encoding. Thus, we did not have to pass it as a parameter. It does not default to the ANSI code page for the current system. The advantage of UTF-8 is that, it handles Unicode characters appropriately, with localized versions of the operating system. The StreamReader class is similar to the Stream class, in that, its Read function accepts an array of chars and not an array of bytes. The return value also remains the same. Thereafter, the StringWriter class concatenates this series of chars. Thus, the StringWriter object w, stores the HTML file as one large string, which gets appended by the Write function. We could also have used the WriteLine function to display the array of chars. Domain Name System

a.csusing System;using System.Net;class zzz{public static void Main(){String s = Dns.GetHostName();System.Console.WriteLine(s);}}

Outputvmukhi

Whenever we need to connect to any computer, the machine's IP address has to be determined. Every machine on the Internet is identified by a unique number, which is called the machine's IP address. This number is of a 'long' data type. Since it is humanly impossible to memorize 4 billion numbers, every machine is also allotted a name. The system that maps a name to its corresponding IP address, is called the Domain Name System (DNS). The class Dns contains only static entities, and hence, it is called a static class. One of its members is called GetHostName, which returns the name of the machine on which the current program is being executed. As we mentioned earlier, every machine by default, is assigned the name of localhost. While installing Windows 2000, we have proactively assigned it the name of 'vmukhi'. Hence, the output shows 'vmukhi'.

a.csusing System;using System.Net;class zzz{public static void Main() {

Page 43: C#   classes

IPHostEntry i = Dns.GetHostByName("www.microsoft.com");IPAddress [] a = i.AddressList;foreach ( IPAddress j in a)System.Console.WriteLine(j.ToString()+ " " + j.Address);}}

Output207.46.130.45 763506383207.46.230.229 -437899569207.46.131.91 1535323855207.46.131.199 -947704113207.46.230.219 -605671729

Every web server or client on the Internet is known by an IP address and a corresponding name. Moreover, the name on the Internet, such as, www.microsoft.com, may not necessarily be restricted to a single IP address. It may have multiple IP addresses mapped onto it. This is what the above program demonstrates. The function GetHostByName is called and given the domain name, whose IP addresses are to be retrieved. An object is returned, which is an instance of IPHostEntry. This class has a property AddressList, which returns an array of IPAddress objects. A single object is assigned to each IP address represented by the domain name. An IP address is a protracted number. It comprises of four numbers, each having a value, which falls within the range of 0 to 255. Thus, we can represent an IP address in the dotted decimal notation, where the four numbers are separated from each other by a dot. Displaying the Address member directly exhibits a long number. You can ascertain as to which of these is more readable. The 'foreach' statement is used to iterate through the array.

a.csusing System;using System.Net;class zzz{public static void Main(){IPHostEntry i = Dns.GetHostByName("www.microsoft.com");String [] a = i.Aliases;foreach ( String j in a)System.Console.WriteLine(j);}}

Outputwww.microsoft.com

An IP address may contain other DNS names, which resolve to the same address. In this case, there are none. Thus, it is evident that the Microsoft site on the net has no other aliases or names.

a.csusing System;using System.Net;class zzz{public static void Main(){IPHostEntry i = Dns.GetHostByName("www.microsoft.com");String a = i.HostName;System.Console.WriteLine(a);}}Outputwww.microsoft.akadns.net

The property HostName gives us the DNS name of the host. The HostName for www.microsoft.com happens to be www.microsoft.akadns.net.

a.csusing System;using System.Net;class zzz{public static void Main(){IPAddress i = IPAddress.Parse("207.46.130.45");System.Console.WriteLine(i.ToString()+ " " + i.Address);

Page 44: C#   classes

}}

Output207.46.130.45 763506383

At times, we may wish to convert a dotted decimal IP address to an int, i.e. an actual long number. The Parse function in IPAddress accepts a dotted decimal IP address as a string, and returns an object that is an instance of IPAddress. We then display the value as a string and an int. How do we achieve the reverse process? We may want to convert an IP Address from a dotted decimal notation, into an instance of IP Address. At other times, we may want to convert an IPAddress, which is in the form of a string, into the dotted decimal notation. The same function can be used to accomplish both these predilections.

a.csusing System;using System.Net;class zzz{public static void Main(){String s = Dns.IpToString(763506383);System.Console.WriteLine(s);}}

Output207.46.130.45

The IP addresses are capable of being converted from an int format into a dotted decimal string or quad representation, by using the IpToString function.

a.csusing System;using System.Net;class zzz{public static void Main(){IPHostEntry i = Dns.Resolve("www.microsoft.com");System.Console.WriteLine(i.AddressList[0].ToString());}}

Output207.46.230.229

The function Resolve in the Dns class, has no complications. We may be interested in an IP address, representing either a single domain name or multiple domain names. One of the solutions is, to use the function GetHostByName. Alternatively, the Resolve function may be used to achieve the same output. Servers and Clients Server program

a.csusing System;using System.Net;using System.Net.Sockets;using System.Text;class zzz{public static void Main(){DateTime n; String s1; TcpListener t = new TcpListener(13); t.Start();while (true){System.Console.WriteLine("Before");Socket s = t.AcceptSocket();System.Console.WriteLine("After");

Page 45: C#   classes

n = DateTime.Now;s1 = n.ToShortDateString() + " " + n.ToLongTimeString();Byte[] b = Encoding.ASCII.GetBytes(s1.ToCharArray());s.Send(b, b.Length, 0);Console.WriteLine("Sent " + s1);}}}

Client program

b.csusing System;using System.Net;using System.Net.Sockets;using System.IO;using System.Text;class zzz{public static void Main(String[] args){TcpClient t = new TcpClient();Byte[] r = new Byte[32];t.Connect("127.0.0.1", 13);Stream s = t.GetStream();int n = s.Read(r, 0, r.Length);String a = Encoding.ASCII.GetString(r);Console.WriteLine("Received " + n );Console.WriteLine(a);t.Close();}}

Server Dos boxBeforeAfterSent 09/13/2001 17:35:48Before Client Dos boxReceived 2009/13/2001 17:35:48

We have supplied the following two programs, which are to be executed in two different DOS boxes:

• a TCP server program. • a TCP client program.

The Server is to be executed first, since the client expects the server to be running. So, lets understand the server program first. The DateTime class provides access to the current date and time, in a multitude of formats. We first create an object, which is an instance of the TcpListener class. Thereafter, the constructor is passed a parameter of 13. It has no other significance, other than being called a Port Number. Most people, when online, either surf the net, or download files, or send E-mail. These activities have certain rules/protocols that are required to be followed for successful execution of the task. We cannot have separate machines to handle each of the above protocols. Therefore, the programs/servers are executed on the same machine; however, they work on a specified number called, the Port Number. Every packet of data is tagged with a number, which specifies the protocol that the packet belongs to. There is a world-wide body called IANA (Internet Assigned Numbers Authority), which determines the number to be assigned to each protocol. Some of them are as follows:

• The World Wide Web has been allocated the number 80• FTP has been allocated the number 21 • E-mail has been allocated the number 25.

Thus, if we notice a packet with a port number 80, we would immediately understand that the packet is carrying data for the World Wide Web, i.e. the www protocol. The TcpListener class constructor requires the Port Number, since our server only accepts packets that carry this number. We have asked our server to listen, and accept packets on port 13. The first 1024 port numbers are reserved by IANA for various protocols. The rest of the numbers may be utilized by us, for our own protocols. The number 13 falls within the range of reserved port numbers.

Page 46: C#   classes

TCP stands for Transmission Control Protocol. Every TCP server needs a function to kick start the entire process. This function is called 'Start'. As of now, we are neither aware of, nor care about what 'start' actually does. We are only apprehensive and aware about the fact that, if this function is not called, none of the clients would ever be able to connect to our server. The TcpListener class is used extensively while writing an Internet server. Around 20 years ago, a large number of networking protocols existed. A networking protocol merely consists of a series of numbers in a specific format, which two machines transmit to each other, in order to facilitate communication. It was extremely complex and cumbersome for programmers to comprehend the numerous networking protocols. TCP/IP was also one such protocol. At that time, nobody would have known that, it would turn out to be the world's most dominant networking protocol, in the days to come. A group of programmers banded together and came out with a series of functions, which could generate the bytes for a large number of networking protocols. For reasons unknown, they named these functions as Sockets API. Thus, a networking coder is traditionally known as a Sockets programmer.In the case of C#, we have a class called Socket. TcpListener is derived from it. The advantage of using socket classes like TCPListener is that, functions like 'Start', call a tremendous amount of code from other Sockets classes. So, we do not have to delve into the innards of sockets programming, in order to be able to use them. The TCPListener class, in a sense, provides us with networking services at a higher level of abstraction, than what could have been achieved, by using the Socket class directly. On running our server ‘a’, we only notice a single word 'Before'. Thereafter, our server seems to wait forever. This wait is attributable to the Accept function. In computer parlance, it goes off to sleep. Our server waits till some TCP client connects to our server on port 13. This wait could extend till eternity. The time has come to try and comprehend as to what a TCP client program is. The TCPClient class is similar to a TcpListener class. This class has functions that use the code from Sockets classes and offer assistance in writing a TCP client program. The Connect function connects to a server. Therefore, it requires the domain name or IP address of the machine, which we desire to connect to. Further, the port number also needs to be specified. We have specified the port number as 13. This is because our server only accepts packets marked with the port number 13. When the Connect function attempts to connect to the server, the server moves beyond the Accept function. The function Accept in the server, returns an instance of a Socket class. This object now becomes a handle, and is used to return data back to the client. The DateTime class has a static member called 'Now', which gives the current date and time. Since the value is a DateTime object, we use one of the many functions in the DateTime class, to display the date as a string in a specific format. Here, we use two functions, ToShortDateString and ToLongDateString; and store the concatenated string value in a variable s1. This string is now required to be sent across to the client. To accomplish this, we need to convert the date in the string, into an array of bytes. So, we first convert the string s1 to an array of chars, and then, employ the GetBytes function to convert this array of chars into an array of bytes. The socket class has a function called 'Send'. It accepts a byte array as the first parameter and the length of the array as the second parameter; and eventually sends the data to the client. At the client end, the GetStream function is used to obtain a Stream object. Then, using the good old Read function, the bytes in the Stream are read into a byte array. Finally, the byte array is converted into a string, and then its contents are displayed. In one of our other C# books, we had demonstrated how the Web client connects to the Webserver on port 80. This program had used a set of classes distinct from the ones used here. But, in the ultimate analysis, they all call code from the Sockets class.

3 Remoting We embark upon this chapter by initiating the concept of 'remoting' and attempting to unravel it by using a diminutive example. In 'remoting', there exists a 'client' program on one machine, which may be located anywhere in the world, and a 'server' program on another machine. The client program thereafter, calls a function in the server program. The server, after having executed the function, delivers the return value of the function, back to the client. To effect and bring the abovementioned scenario to fruition, we have employed three programs named o.cs, s.cs and c.cs. Furthermore, to avoid calling the compiler command for each one of them individually, we have placed the compile commands in a batch file named a.bat.

o.cs

Page 47: C#   classes

using System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class zzz : MarshalByRefObject { public zzz() { Console.WriteLine("Constructor"); }~zzz() {Console.WriteLine("Destructor");} public String abc() { Console.WriteLine("Vijay Mukhi2"); return "Sonal"; } } s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class sss {public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class ccc { public static void Main() { TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); zzz a = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee"); if (a == null) System.Console.WriteLine("Could not locate server"); else Console.WriteLine(a.abc()); } } a.batcsc.exe /t:library o.cscsc.exe s.cscsc.exe /r:o.dll c.cs

Output in server dos boxPress <enter> to exit...ConstructorConstructorVijay Mukhi2 DestructorDestructor Output in client dos boxSonal

We simulate the 'remoting' behaviour by bringing two dos boxes into play. We run the client program in one box and the server program in the other box.

Page 48: C#   classes

The file o.cs is created with a class zzz, which derives from the class MarshalByRefObject. This abstract class, belonging to the System namespace, cannot be used in isolation. Hence, it requires a class that derives from it. A class of this kind permits remote objects to call code from it. There exist a constructor and destructor, which furnish information regarding the time of creation and destruction of an object. A function called abc is created, which displays 'Vijay Mukhi2' by using the WriteLine function and then, returns the name of 'Sonal'. Other programs can call the function abc, merely by deriving a class from MarshalByRefObject. The file o.cs is compiled into a dll, using the /target:library option. Understanding the abovementioned explanation does not require the genius of a rocket scientist. We shall now go ahead and write a simple server program named s.cs. c is an object, which is an instance of class TcpChannel, in the namespace System.Runtime.Remoting.Channels.Tcp. The constructor is assigned a port number, on which, the other machine can establish communication with it. The protocol used here is known as TCP/IP, where TCP stands for Transmission Control Protocol and IP implies Internet Protocol. The port number facilitates the creation of a channel between two machines. The number that we have selected for the port is 8085. This is because all the samples provided by Microsoft use this number. It must obviously be a lucky mascot for that Company. You may select any other number, as long the same number is used for the client and the server. Hereinafter, whenever a client connects on port 8085, the TCP/IP stack or the Windows Internet Software keeps the server apprised about the connection. The server, in turn, keeps a listening vigil on port 8085. The namespace, System.Runtime.Remoting contains a class ChannelServices, which has a static function RegisterChannel. This function accepts an IChannel interface as a parameter and thereafter, registers the Channel object with the channel services. The main activities of this function are not known. It suffices to say that, without this function, the TCPChannel object that has been created, would not be effective. The class TCPChannel derives from a large number of interfaces, such as IChannelSender, IChannel, IChannelReceiver, IDictionary, ICollection and IEnumerable. The most imperative function that the server has to call is, the one that registers the function abc in class zzz, contained in the file o.dll. The name of this static function is RegisterWellKnownServiceType, of the class RemotingConfiguration, belonging to the namespace System.Runtime.Remoting. This function takes three parameters, beginning with a type object that represents the name of the assembly that contains the object to be registered. Our object is located in a dll. It could even be located in an executable. The name 'o' of the dll file is passed as the second parameter to the GetType Object. The first parameter to this function is the name of the class that represents the object. In case the class is located in a namespace, the full name has to be furnished. The GetType object returns a Type object. The second parameter of the function RegisterWellKnownServiceType, represents a URI, or a Universal Resource Identifier. It could be any original word that helps in identification of functions in the class zzz. This is referred to as an 'endpoint', where the object shall be published. The client does not connect to the class zzz directly, but to an endpoint. In our case, the URI is eee. It points indirectly to the class zzz, which is the object that needs to be remoted. Any string may be used, provided the same string is used by the client too. Had we wished to connect using ASP.Net, we would have been compelled to use zzz.soap, as the endpoint. The last parameter specifies the mode, which may assume any of the two values, i.e. SingleCall or Singleton. The data type is an enum named WellKnownObjectMode, which only embodies the two abovementioned values. The mode specifies the 'lifetime' of the object, i.e. the frequency of its creation. If we specify the mode as SingleCall, a new instance of the object zzz will be created each time a message is received. If it is assigned the value of Singleton, an instance of zzz will be created only once, and all the clients shall interact with it. On compiling s.cs, an executable named s.exe will be generated. The client code, which calls code from the remote object is placed in the file c.cs. In this file, we commence with the 'using' statement, in order to introduce the namespaces for the classes. Thereafter, a TcpChannel is created, but unlike a server, it is devoid of a port number. The port number assigned to the client is of no significance, since it is not binding on the client to listen to any request. Therefore, a random value is assigned as the port number. RegisterChannel registers the TcpChannel c. Next, we use the static function GetObject from the Activator class in the system namespace. This function accepts two parameters:

• The first parameter is of data type Type, which represents the class that we wish to instantiate.• The second parameter is the URI of the object, which is to be instantiated.

The URI follows a specific sequence, which is as follows:

• It commences with the protocol that is required for communication. In our case, it is tcp. • This is followed by the machine name on which the server resides.

In our case, everything is located on a single machine. Hence, we use the machine name 'localhost'. The machine name is by suffixed with a colon.

Page 49: C#   classes

• This is succeeded by the port number. • Finally, the endpoint created in the server is to be specified. In this case, it is eee.

The abovementioned sequence has to be maintained, and all the above items are mandatory. This function returns an object that is an instance of class zzz, which we store in object 'a'. If the object 'a' has a value of null, it generates an error and displays a suitable error message. Otherwise, the function abc of the object 'a' is called, and the return value is displayed using the WriteLine function. On compiling c.cs, an error is generated. So, we provide a reference to the dll named o.dll, since the class zzz is present in it. In one of the dos boxes, we first run the server 's'. On the surface of it, it appears as though only the string "Press <enter> to exit" has been displayed. But in the background, the server has been registered as a sink for all those functions that need to call the function abc, from its endpoint eee. If we merely press the Enter key, the ReadLine function, whose raison d'être is to prevent our server from quitting out, actually exits. When we run the client in a separate dos box, the server dos box displays 'Constructor' twice. This establishes the fact that the server has located the class zzz in the assembly o.dll and instantiated it. The server instantiates the object that is to be remoted, to enable the framework to read the metadata present in the assembly. This is part of its registration process. Thereafter, it displays the string 'VijayMukhi 2' in the server dos box, since the client has called the function abc. The client dos box displays 'Sonal', which is the return value of the function. The client program quits out gracefully, whereas, the server cools its heels, waiting for other calls. The server destroys the current object, and the framework keeps a listening vigil for other clients, which may be eager to connect on previously registered channels. When the Enter key is pressed, it results in the termination of the server program. Thus, the destructor gets called. We are, however, not certain whether an object perishes or survives when a program quits out. In any case, as far as the C# language is concerned, this is not in our control. The Constructor gets called twice:

• On the first occasion, the framework reads the metadata .• On the second occurrence, the client interacts with an instance of the object zzz.

The Destructor also gets called twice, since the framework brazenly destroys the two zzz objects which had been created. The client has to initially locate the server, and thereafter, connect to it, so that the zzz object gets instantiated. The client requires a function to execute this task and to return an object, which is an instance of zzz. However, the actual object dwells on another computer, which could be ensconced at a geographically dispersed location. Thereafter, the function GetObject returns a simulation of the zzz object, which it instantiates. For all practical purposes, it is under the delusion that it is the real zzz object, though in reality, the object has been indirectly created on another machine. Thus, GetObject returns a 'proxy' for the remote object. The literal meaning of the word 'proxy' is 'a surrogate of the original'. This proxy then directs the call to the original object residing on an alternative machine. The return value of the GetObject function is indicative of whether the object was successfully created on the remote machine or not. A null value indicates an error. The function abc is called from the remote zzz object, after the object has been successfully instantiated. However, a.abc() actually interacts with the proxy object, which in turn, forwards the call to the remote machine. The remotely located program executes the function abc, and thereafter, dispatches the return value over to the client. Two other programs named 'proxy' and 'stub' are also brought into play, in order to accomplish the task of 'remoting'. Let us now analyze this process from a fresh perspective. At the outset, we need to create two sub-directories, r2 and r3.

c:\csharp>md r2c:\csharp>md r3

Next, we are required to copy the server file s.cs and o.cs to the sub-directory r2, and the client file c.cs to the sub-directory r3.

C:\csharp>copy s.cs r2C:\csharp>copy o.cs r2C:\csharp>copy c.cs r3 C:\csharp\r2> csc s.csC:\csharp\r2> csc /t:library o.cs C:\csharp\r3> csc c.cs

Page 50: C#   classes

c.cs(11,7): error CS0246: The type or namespace name 'zzz' could not be found (are you missing a using directive or an assembly reference?)c.cs(12,11): error CS0103: The name 'a' does not exist in the class or namespace 'ccc'c.cs(15,19): error CS0246: The type or namespace name 'a' could not be found (are you missing a using directive or an assembly reference?)

On compiling the two files placed in the sub-directory r2, no errors are generated. However, on compiling the client program in the sub-directory r3, an error is generated. We hit a roadblock at this juncture, since the file c.cs requires the assembly file in which, the remoted object, o.dll resides. You may argue that the prerequisite of placing the dll in the client's sub-directory defeats the very purpose of 'remoting'. However, the presence of the file is obligatory only for the metadata of the class zzz. The motivation for doing this will be elucidated shortly.

C:\csharp\r3> copy c:\csharp\r2\o.dll C:\csharp\r3> csc c.cs /r:o.dll

Therefore, we copy the assembly file o.dll from the sub-directory r2, into the sub-directory r3, and thereafter, recompile it. Now, we add a tangy dash of lime to the mundane and bland routine given above, to give it a slight twist. The string VijayMukhi2 is modified to VijayMukhi3. The library is recompiled to produce a dll file in the sub-directory r2.

C:\csharp\r2>edit o.cs

public String abc() { Console.WriteLine("Vijay Mukhi3"); return "Sonal";} C:\csharp\r2>csc /t:library o.cs

As a consequence, the copies of the function abc, which reside in the two subdirectories r2 and r3, become dissimilar.

Output in server dos boxPress <enter> to exit...ConstructorConstructorVijay Mukhi3 DestructorDestructor Output in client dos boxSonal

The server program and the client program are run in their respective directories. On doing so, it becomes evident that, even though the client had a copy of the dll in its own sub-directory, it still called the function abc from o.dll, which was resident in the sub-directory of the server. Therefore, the output displayed is 'Vijay Mukhi3', and not 'Vijay Mukhi2'. In real life, the code placed in the directories r2 and r3 would actually be dwelling within geographically dispersed machines. The metadata of the object should be provided to the client, for the sole purpose of compilation.

o.csusing System;namespace nnn{public class zzz : MarshalByRefObject{public zzz(){Console.WriteLine("Constructor");}~zzz(){Console.WriteLine("Destructor");} public String abc() {Console.WriteLine("Vijay Mukhi2");

Page 51: C#   classes

return "Sonal " ;}public String pqr(string s) {Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ;}} } s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class sss {public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;using nnn;public class ccc{ public static void Main() { TcpChannel c = new TcpChannel(); ChannelServices.RegisterChannel(c); zzz a = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee"); if (a == null) System.Console.WriteLine("Could not locate server"); else Console.WriteLine(a.abc());Console.WriteLine(a.pqr("hell")); } }

Output ClientSonalVMCI hell Output in server dos boxPress <enter> to exit...ConstructorConstructorVijay Mukhi2ConstructorSonal Mukhi2 DestructorDestructorDestructor

We shall now initiate a few modifications in the three files, o.cs, s.cs and o.cs. o.cs : Microsoft insists that all classes must reside in a namespace. Therefore, class zzz has been made part of the nnn namespace. A function named pqr has been introduced, which accepts a string parameter and returns the same string. The return value is prefixed with the acronym 'VMCI', which stands for my institute, i.e. Vijay Mukhi's Computer Institute. s.cs : The server has the name of the class nnn.zzz located within the function RegisterWellKnownServiceType. It has no regard for the functions belonging to the class zzz, since its primary focus is on the endpoint that represents the class.

Page 52: C#   classes

c.cs : Internally within the client, the class has been renamed as nnn.zzz. Since we abhor the process of writing the same ungainly names repeatedly, we use the keyword 'using' with nnn. As a result of this, every occurrence of zzz is replaced by nnn.zzz. So, the typeof keyword encounters the class name nnn.zzz. The function pqr is called with a single parameter. It is imperative to have the metadata available at this stage, since it contains the following:

• The name of the class• All the functions that the class carries• The signatures of the functions.

This is a form of code validation, which ensures that inappropriate signatures are not used while the functions in the client are being called. The output generated by these two programs is very predictable. There is just a slight departure from the expected output, in the case of the server window. Whenever a function from the remote server is to be executed, the constructor in the class is called. However, the output of the destructors is highly unpredictable. Therefore, it is possible for us to pass parameters to functions, even if the function is being remoted. Its demeanor is very similar to that of calling the function off the same machine. Interfaces

sh.csusing System;namespace nnn{public interface iii{String pqr(String s);}}

o.csusing System;namespace nnn {public class zzz : MarshalByRefObject, iii{public zzz(){Console.WriteLine("Constructor");}public String pqr(string s){Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ;}} } s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class sss {public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c);RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } c.csusing System;using System.Runtime.Remoting;

Page 53: C#   classes

using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;using nnn;public class ccc{public static void Main(){TcpChannel c = new TcpChannel();ChannelServices.RegisterChannel(c);iii a=(iii)Activator.GetObject(typeof(nnn.iii),"tcp://localhost:8085/eee");if (a == null)System.Console.WriteLine("Could not locate server");else{Console.WriteLine(a.pqr("hell"));}}}

a.batdel *.exedel *.dllcsc.exe /t:library sh.cscsc.exe /t:library /r:sh.dll o.cscsc.exe s.cscsc.exe /r:sh.dll c.cs

Output ServerPress <enter> to exit...ConstructorConstructorSonal Mukhi2 Output ClientVMCI hell

Interfaces are employed to overcome the requirement of placing the entire code of a class in an assembly, which is resident on the client machine. In the file sh.cs, a simple interface called iii is created in the namespace nnn, possessing a single function pqr. It is imperative for all classes that derive from this interface, to incorporate this function. The interface iii is compiled to a dll, and it does not require the /r: switch, since it does not refer to any external modules. The file carries only the metadata, and does not contain any code. In the object o.cs, class zzz is derived from both, the class MarshalByRefObject and the interface iii. Thus, it is essential to implement the function pqr in the class zzz. While compiling the file o.cs, a reference must be provided to the file sh.dll, which contains the metadata for the interface iii. The server does not get affected in the least by any modifications carried out in the object. This is by virtue of the framework, which registers a class zzz and not the interface iii. There is no necessity for the server to refer to any of the assemblies of o.dll and sh.dll. In the client program, the GetObject function instantiates an object of type iii, and not from the class zzz. Whether the code is called from an interface or a class, is of no significance. The prime benefit of using an interface is that the C# compiler does not need to introduce the assembly o.dll, which contains a sizeable amount of code. The assembly file sh.dll is adequate, since it provides the metadata of the interface. For example, Jack can create the interface iii on a separate machine and then, send across the file sh.dll to his client Jil. She can, in turn, use it on her machine, thereby meeting the requirements of the complier. The endpoint represents a zzz class, which in turn, represents an interface named iii, as well as, an object called MarshalByRefObject. A request for an iii object in the client does not generate any error, since the endpoint represents a zzz instance, which encompasses an iii instance. We use the file a.bat to compile all the above four files with a single stroke. This program evinces how we can separate the definition from the implementation. For the next program, the file s.cs remains unchanged.

o.csusing System;namespace nnn{

Page 54: C#   classes

public class zzz : MarshalByRefObject{public zzz(){Console.WriteLine("Constructor");}public String pqr(string s){Console.WriteLine("Sonal Mukhi2"); return "VMCI " + s ;}} } c.csusing System;using System.Threading;using System.Runtime.Remoting;using System.Runtime.Remoting.Messaging;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;namespace nnn{public class ccc{public static ManualResetEvent e;public delegate String ddd(String s);public static void Main(string [] args){e = new ManualResetEvent(false);TcpChannel c = new TcpChannel();ChannelServices.RegisterChannel(c);zzz o = (zzz)Activator.GetObject(typeof(zzz), "tcp://localhost:8085/eee");if (o == null)System.Console.WriteLine("Could not locate server");else{AsyncCallback cb = new AsyncCallback(ccc.abc);ddd d = new ddd(o.pqr);IAsyncResult ar = d.BeginInvoke("Vijay", cb, null);Console.WriteLine(ar.IsCompleted);}e.WaitOne();}public static void abc(IAsyncResult ar){ddd d = (ddd)((AsyncResult)ar).AsyncDelegate;Console.WriteLine(d.EndInvoke(ar));Console.WriteLine(ar.IsCompleted);e.Set();}}}

Output ClientFalseVMCI VijayTrue Output ServerPress <enter> to exit...ConstructorConstructorSonal Mukhi2

In one of the earlier examples, we had called the pqr function from our client. At this point, a network message was transmitted to the server, which executed the function abc and dispatched the outcome back to the client. There is a likelihood that the function executed in the server, could have taken hours to complete execution. So, the client would have had to wait eternally for the server. Therefore, this method is prodigiously wasteful in terms of time and resources. Thus, whenever a 'synchronous call' is made to the remote object, the client has to cease all activities, till it receives a response from the server.

Page 55: C#   classes

It would prove to be a lot more efficacious if the client could be allowed to continue doing its job, while the server is busy executing the function. On completion of the task, the server could notify the client. In other words, we do not want the client to 'wait' or 'block'. This mechanism can be implemented by employing an 'asynchronous call' to the function. In such cases, the call is made by the client. Therefore, only the code in the client needs modification. In the chapter on Threads, we had learnt about the ManualResetEvent event object. We had set it to a value of False. In such situations, the client will wait on the WaitOne function, till the Set function is called. A delegate is a type safe way of calling a method in an oblique manner. We have created a delegate ddd that accepts a string as a parameter, and it returns a string. The framework can execute a function in an asynchronous mode provided, we represent it as a delegate. Here, the callback function has been represented by the use of a delegate. For those who have earned their spurs on C/C++, it would be a revelation that delegates are actually 'pointers to functions'. The function pqr, which has been called in an asynchronous mode, accepts as well as returns a string. Therefore, it is amply evident that the delegate uses the same parameters and return types. When we were writing a book on Intermediate Language, IL (an assembler language to which all code in the .NET world gets converted), we had discovered that a delegate finally gets converted into a class. In our case, the delegate ddd contains a large number of functions, which get added to it. Two such functions are, BeginInvoke and EndInvoke. The main role of these functions is to call code that is written by the system or runtime. Hence, they are called native functions. Thereafter, an object cb, which is an instance of a delegate AsyncCallback, is created. The constructor of this class is passed a function abc, which is notified as soon as the remote function completes execution. The callback function abc is passed an IAsyncResult parameter, since this function matches the declaration of the delegate. Next, we create a delegate d of type ddd, whose constructor is assigned the function name pqr, which is to be executed in an asynchronous manner. The BeginInvoke function is called next. It is passed the parameter string, which is to be transmitted to the remote function named pqr. The second parameter to this function is another function called cb, which also requires to be notified after all the action has been wrapped up. This delegate allusively represents the function abc in the class ccc. The BeginInvoke function returns an IAsyncResult, which lies inert at this stage. The parameters passed to it are similar to the ones provided to our event handling function abc. The remote function may take excessive time to execute. Therefore, to avoid wasting time, the client continues with its work. Once the call is completed, the framework ensures that the function abc is called with the parameter representing the return value. To figure out the return value, we simply cast the parameter to a ddd object, and call the function EndInvoke off it. This return value is displayed in the client dos box. For Asynchronous calls, we create two delegates, one for the callback function, and the other for the remote method. Function BeginInvoke calls the remote function, which in turn, calls the function abc. When the call ceases, EndInvoke is called to receive the return values. The IAsyncResult consists of five properties, one of which is IsCompleted. Initially, this property returns False, since the asynchronous call has not been completed. However, in the function abc, it returns True, since the remote call has run its course and is done with. The EndInvoke function plays no role in ending the remote call. If we display the value contained in the IsCompleted property, prior to the EndInvoke function, a value of True would get displayed.

sh.csusing System;namespace nnn{public class fff : MarshalByRefObject{public void xyz(String t){Console.WriteLine(t);}}} >csc /t:library sh.cs o.csusing System;using System.Runtime.Remoting;namespace nnn{public class zzz : MarshalByRefObject{public zzz(){Console.WriteLine("Constructor");}

Page 56: C#   classes

public String pqr(String s,fff f){f.xyz("Hi");Console.WriteLine("pqr called");return "VMCI " + s;}}} >csc /t:library o.cs /r:sh.dll s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class sss {public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("nnn.zzz,o"), "eee", WellKnownObjectMode.SingleCall); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } } >csc s.cs c.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;namespace nnn{public class ccc{public static void Main(string [] args) {TcpChannel c = new TcpChannel(8086);ChannelServices.RegisterChannel(c);fff f = new fff();zzz o = (zzz)Activator.GetObject(typeof(nnn.zzz),"tcp://localhost:8085/eee");if (o == null)System.Console.WriteLine("Could not locate server");elseConsole.WriteLine(o.pqr("Vijay",f));}}}

>csc /r:sh.dll /r:o.dll c.cs Output in server dos boxConstructorConstructorPqr called Output in client dos boxHiVMCI Vijay

More often than not, the code for the server remains unchanged. In this program, our objective is to call a function remotely, having a parameter which is of a 'user-defined' type. Therefore, in the file sh.cs, we have a class called fff, which is derived from the class MarshalByRefObject. It contains a simple function called xyz, which displays a string that is passed as a parameter. The program centers around passing an instance of a user-defined class fff, as a parameter to the function pqr. In the object file o.dll, the class zzz has a function pqr, which accepts two parameters viz. a string s, and an object f. The object f is an instance of the class fff. Using the object f, the xyz function is called. This function is passed the string 'hi'. The rest of the code remains unaltered. The server, as mentioned earlier, is not modified.

Page 57: C#   classes

The client c.cs merely creates an object f, as an instance of class fff. This object is then passed as a parameter to pqr, without paying any cognizance to the fact that, the data shall be sent to another computer located in some other part of the world. The client couldn't care less, since this class is derived from the class MarshalByRefObject. This is the only prerequisite imposed on it, when instances of user-defined type are used as parameters. While compiling, references are to be given to the files o.dll and sh.dll, since both contain the metadata information required for error checking. The output in the client box displays 'Hi'. You should note that a port number has been furnished in the client program. Earlier, the port number had been provided only in the server program. By having two ports registered, the framework can use the two port numbers for different purposes. While one port number may be used to enable the Client to pass parameters to the server, the other may be used to permit the server to pass parameters to the client. This facilitates bi-directional communication between servers and clients, thereby, permitting the use of remote parameters. Passing by value

sh.csusing System;namespace nnn{[Serializable]public class fff{int j = 1;public fff(){Console.WriteLine("Constructor " + j);}public void xyz(){j++;}public int aaa(){return j;}}} o.csusing System;using System.Runtime.Remoting;namespace nnn{public class zzz : MarshalByRefObject{public zzz(){Console.WriteLine("Constructor");}public fff pqr(fff o){o.xyz();o.xyz();return o;}}} c.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;namespace nnn{public class ccc{public static void Main(string [] args){

Page 58: C#   classes

TcpChannel c = new TcpChannel(8086);ChannelServices.RegisterChannel(c);fff p = new fff();zzz o = (zzz)Activator.GetObject(typeof(nnn.zzz),"Tcp://localhost:8085/eee");if (o == null)System.Console.WriteLine("Could not locate server");else{Console.WriteLine("Before " + p.aaa());fff a = o.pqr(p);Console.WriteLine("After " + a.aaa() + " " + p.aaa());}}}}

Server OutputConstructorConstructor Client OutputConstructor 1Before 1After 3 1

In the file sh.cs, we have two functions, aaa and xyz, and a single variable j. Their roles are as follows:

• The constructor displays the value of variable j.• The function xyz increments its value by one. • The function aaa returns the value of the variable.

The variable j could have been accessed directly, by making it public. Instead, the program uses functions to access it. The fff class, for some reason, has to be made Serializable. In the file o.cs, the object zzz contains a function pqr. This function accepts an fff instance as a parameter, and then returns a object of the same type. The xyz function in fff is called twice. An object that is an instance of a particular class, is different from another object, which may be an instance of the same class. There is no difference in the functions contained in the objects, since the code is identical amongst the instances. The variation occurs by virtue of the values of the instance variables, contained in these objects. Thus, the variable j will possess dissimilar values in the varied instances of the same class. In the client, a new fff instance named 'p' is created. The constructor is called, which results in display of the text "Constructor 1", since the current value of the object j is 1. The value in j is again printed, by calling the function aaa. Thereafter, function pqr is called with the parameter p, which is an fff object. The value of the variable j, which is contained in p, is presently 1. The function pqr calls the function xyz from the class fff twice. As a consequence of this, the value of j increases by 2. The fff instance, which is returned back by this function, is stored in 'a'. The WriteLine function then prints the value of j from both the fff objects, p and a. The object p is local to the client. Therefore, p.aaa would always display the value 1. The object 'a' that has been 'remoted', prints the value of j as 3. The names of objects are of no consequence at all, across computers. They are not visible in the IL code generated either. While remoting, the values in the local objects get handed over to a remote function. The modified value of the variable is displayed only in the new instance of the object. The local instance continues to display the original value. Singleton

o.csusing System;namespace nnn{public class zzz : MarshalByRefObject{public zzz(){Console.WriteLine("Constructor");}~zzz(){Console.WriteLine("Destructor");} public String pqr() {

Page 59: C#   classes

Console.WriteLine("pqr"); return "VMCI ";}}} s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;public class sss {public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("nnn.zzz,o"), "eee", WellKnownObjectMode.Singleton); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } }

c.csusing System;using System.Threading;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;namespace nnn{public class ccc{public static void Main(string [] args){TcpChannel c1 = new TcpChannel();ChannelServices.RegisterChannel(c1);zzz o = (zzz)Activator.GetObject(typeof(zzz),"tcp://localhost:8085/eee");Console.WriteLine(o.pqr()); Console.WriteLine(o.pqr()); }}}

Server OutputPress <enter> to exit...Constructorpqrpqr Destructor Client OutputVMCIVMCI

The object zzz, which is to be remoted, resides in file o.cs and has a single function pqr, which returns 'VMCI'. It also contains a constructor and a destructor. However, its contents are nothing earth shaking.We have only carried out a single modification in the server program. The last parameter to the function RegisterWellKnownServiceType is changed to Singleton. This is suggestive of the fact that, all requests to the remoting object are to be handled by the same instance of zzz. It does not create a separate instance of the object, every time the framework receives a request. As the name itself suggests, Singleton means 'single' or 'only one entity'. The client calls the function pqr twice. However, the constructor is called only once at the beginning, in order to read the metadata. The object is not created repeatedly on each request. Therefore, neither the constructor, nor the destructor is called.

s.csusing System;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Tcp;

Page 60: C#   classes

using System.Runtime.Remoting.Channels.Http; namespace nnn { public class sss { public static void Main() { TcpChannel c = new TcpChannel(8085); ChannelServices.RegisterChannel(c); HttpChannel c1 = new HttpChannel(8086); ChannelServices.RegisterChannel(c1); RemotingConfiguration.RegisterWellKnownServiceType(Type.GetType("nnn.zzz,o"),"eee", WellKnownObjectMode.Singleton); System.Console.WriteLine("Press <enter> to exit..."); System.Console.ReadLine(); } }} c.csusing System;using System.Threading;using System.Runtime.Remoting;using System.Runtime.Remoting.Channels;using System.Runtime.Remoting.Channels.Http;using System.Runtime.Remoting.Channels.Tcp;namespace nnn{public class ccc{public static void Main(string [] args){HttpChannel c = new HttpChannel();ChannelServices.RegisterChannel(c);TcpChannel c1 = new TcpChannel();ChannelServices.RegisterChannel(c1);zzz o = (zzz)Activator.GetObject(typeof(zzz),"http://localhost:8086/eee");zzz o1 = (zzz)Activator.GetObject(typeof(zzz),"tcp://localhost:8085/eee");Console.WriteLine(o1.pqr()); Console.WriteLine(o1.pqr()); Console.WriteLine(o.pqr()); Console.WriteLine(o.pqr()); }}}

Server OutputPress <enter> to exit...Constructorpqrpqrpqrpqr Destructor Client OutputVMCIVMCIVMCIVMCI

The server has created two channels; one is the normal channel named TcpChannel, while the other one is called an HttpChannel. A separate port number has to be allotted to each of these channels. Therefore, the number 8085 is assigned to the TCP channel, while the number 8086 is assigned to the HTTP channel. The registration process does not delve into mundane issues, such as, the type of channel that has been used. The client creates both, an HttpChannel object, as well as a TcpChannel object. However, it does not specify the port numbers to the constructors. The GetObject function is used to specify a proxy. A major modification that has been incorporated here is, the HttpChannel being prefixed with http:, and the TcpChannel being prefixed with tcp:. The endpoint URI is retained as eee. The http channel connects to the server on port 8086, while the tcp channel connects to the server on port 8085. If you interchange the port numbers, the program will pause endlessly.

Page 61: C#   classes

The pqr function is called, by utilizing the http channel and the tcp channel. Since the call is made 4 times, 'pqr' is also displayed 4 times on our screen.We have not used a stopwatch to time the output generation; however, we were able to conclude that, the HTTP (Hyper Text Transfer Protocol), is much slower than the TCP (Transmission Control Protocol). This difference in the speeds of HTTP and TCP occurs because HTTP uses both SOAP (the Simple Object Access Protocol) and XML, which slows it down, whereas, TCP uses a binary protocol, which makes it relatively faster. A Singleton object preserves its state between function invocations, since it is not created each time that the function is invoked. A port number is not specified while registering a client channel, since the framework assigns it internally. This is dependent upon the task to be performed, such as, listening or connecting. The framework connects to a remote channel, using the URI specified. If we actually specify a port number in the client, it starts behaving like a server, i.e. it too listens on the specified port.

4 Controls Windows Forms or Winforms is a contemporary Windows-based forms package that endows the Windows programmer with an innovative methodology for creating aesthetic user interfaces and interactive applications. We will not ramble on about the pros and cons of the package, but commence the creation of the smallest GUI (Graphical User Interface) application.

a.cspublic class zzz{public static void Main() {zzz z = new zzz();System.Windows.Forms.Application.Run(z);}}

Run the compiler as >csc a.cs Compiler Errora.cs(6,1): error CS1502: The best overloaded method match for 'System.Windows.Forms.Application.Run(System.Windows.Forms.Form)' has some invalid argumentsa.cs(6,38): error CS1503: Argument '1': cannot convert from 'zzz' to 'System.Windows.Forms.Form'

An error is generated because, Run, which is a static function in the Application class of the System.Windows.Forms namespace, requires a Form object. The error distinctly states its inability to convert a zzz to System.Windows.Forms.Form, which proves that, an object that looks like Form is mandatory here, and not zzz.

a.csusing System.Windows.Forms;public class zzz : Form{public static void Main() {Application.Run(new zzz());}}

Page 62: C#   classes

Screen 4.1This program is not very dissimilar from the previous one. The using keyword is employed to avoid the inevitability of writing namespace with every object. The object z has no efficacy here, since we are passing the zzz object directly to the Run function. As the class zzz is derived from Form, no error is generated. When we run the program, a small blank window is displayed. You can click on the 'x' symbol to close it. The output is substantial enough for a single line of code.

a.csusing System.Windows.Forms;public class zzz: Form {public static void Main(){Application.Run(new zzz());}zzz() {Text = "Vijay Mukhi";}}

Screen 4.2 In the constructor of the zzz class, we have initialized a member called Text to the string value 'Vijay Mukhi'. When we run the program, to our amazement, our window, which earlier was without a title, now possesses the title 'Vijay Mukhi'. This is the introductory concept of Windows Forms programming. The class called Form has abundant properties such as Text etc., which have specific relevance in a window. Any modifications to the properties get reflected immediately in the window. The changes depend upon the properties that we modify. In this case, the property of Text changes the Caption, text displayed in the title bar. A Form represents a window displayed by an application. An application can have different types of windows, such as a standard, tool bar, borderless or floating window. The Form class is versatile enough to handle all the above types of windows, as it is derived from innumerable classes. A Dialog Box, which is used to accept input from the user, is available in two modes viz. modal and modeless. Our trustworthy Form class can also handle such Dialog boxes with equal aplomb. We normally use the Form class as the preliminary class for building WinForms Applications.

Page 63: C#   classes

The Main method calls the Run function and gives it the Form object. In the constructor, we can modify the properties of the Form Class to give the window a desired appearance. Since these properties are not static, they cannot be altered in Main, but can be modified in the constructor or in any other function.

a.csusing System.Windows.Forms;public class zzz{public static void Main(){Application.Run(new yyy());}}class yyy : Form{public yyy() {Text = "Vijay Mukhi";}}

Screen 4.3 In this program, we have created another class yyy that derives from the Form class. We have used this object as a parameter to the function Run. The rules do not impel us to derive the class zzz from Form. However, in all our programs, we shall follow the first approach since we have decided to steer clear of controversy and stick to the rules.

a.csusing System.Windows.Forms;public class zzz: Form {public static void Main(){Application.Run(new zzz());}zzz() {ClientSize = new System.Drawing.Size(300,600);Size = new System.Drawing.Size(100,200);}}

Page 64: C#   classes

Screen 4.4 The above example sets two properties of the Form class. The first, which is called ClientSize, is used by Windows.Forms to decide how large our initial window would be. This property has a default value, which can be overwritten by specifying the width and height. As we need to furnish two values, we use a class called Size in the namespace System.Drawing, which accepts two values. This class does not insist on receiving meaningful values. The constructor is passed the width and height of the desired window in pixels. A graphics screen is divided into small dots or pixels. Depending upon the configuration of the monitor and graphics card, a computer can handle and display a certain number of pixels and colors. The higher the configuration, the larger are the number of pixels and colors that are available. The size of the client area of the form is computed as the size of the form minus the borders and the title bar placed by Windows. They are not of our concern, since we shall be placing our own controls in our form. ClientSize is a property with a default value, and it gets updated automatically whenever the form is resized. The next property is Size. The user enjoys the flexibility of altering the size of the window at run time. Size is initialized in manner similar to ClientSize.

a.cs using System.Windows.Forms;using System.Drawing;public class zzz: Form {Button b;public static void Main(){Application.Run(new zzz());}zzz() {b = new Button();Controls.Add(b);}}

Screen 4.5

Page 65: C#   classes

We now see a small button at the top left hand corner of our Window. How did we create this button? To do so, we first, create an object b, that looks like a Button class. The Form class has a large number of properties such as ClientSize, Size etc. One of them is called Controls, which is a read-only property since it contains only a Get. This property returns a Control.Collection object, whose Add function adds the control to the Client area of the window. We shall be sprucing up our button shortly.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {Button b;public static void Main(){Application.Run(new zzz());}zzz() {b = new Button();b.Location = new Point(100,200);b.Size = new Size(100,50);b.Text = "Vijay Mukhi is smart";Controls.Add(b);}}

Screen 4.6 Anything that is placed on a form is called a Control or a Widget. Similar to a Form, a button control, popularly known as a command button, has numerous properties. One of them is the Location property, which decides the position on the Client area where the button will be positioned. Here, we use the Point class and not Size, even though both are objects that represent two numbers. By convention, a Size object represents a width and a height and a Point object has an x and y co-ordinate system, starting from the upper left corner. Most properties have a default value. Since this fact about default values has been reiterated numerous times, we shall not repeat it again. The Size property determines the initial size of the window and the string assigned to the Text property is displayed on the button.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {Button b;public static void Main() {Application.Run(new zzz());}zzz() {b = new Button();b.Location = new Point(100,200);b.Size = new Size(100,50);

Page 66: C#   classes

b.Text = "Vijay Mukhi is smart";b.Click += new System.EventHandler(abc);Controls.Add(b);}public void abc(object s, System.EventArgs e){MessageBox.Show("Hi");}}

Screen 4.7 In the earlier example, clicking on the button was an exercise in futility because the button did not achieve anything. After augmenting the code of the program, when we click on the button, we see a MessageBox that displays the greeting 'Hi'. The rationale behind a button control is that, when we click on it, some code should get executed, some action should take place. The button class has an event object called Click, which accepts an object of type EventHandler. The syntax for events uses the += symbol to add a function that is to be called when the event handler gets activated. The function name is given through the EventHandler delegate. This delegate has been specially created only to handle events that a control will generate. Thus, the function abc, which is passed as a parameter to the EventHandler delegate, must have a certain signature. The first parameter is the generic object that could represent any entity identifying the caller. The second parameter is an EventArgs object, which we will explain shortly. Thus, each time we click on the button, the function abc gets called. This function in turn calls the static function Show from the MessageBox class to display 'Hi'.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {Button b;public static void Main(){Application.Run(new zzz());}zzz() {b = new Button();b.Location = new Point(100,200);b.Size = new Size(100,50);b.Text = "Vijay Mukhi is smart";b.Click += new System.EventHandler(abc);b.Click += new System.EventHandler(pqr);Controls.Add(b);}public void abc(object s, System.EventArgs e){MessageBox.Show("Hi");}public void pqr(object s, System.EventArgs e){MessageBox.Show("Bye");} }

Page 67: C#   classes

Screen 4.8 This program reveals the veritable power of events and delegates. Two functions, abc and pqr, are called whenever the button is clicked. To achieve this, all that we need to do in the code is to call the Click event again, using the += symbol, followed by the name of the new function. The -= symbol is used if we change our minds. This is a type safe way of calling code in response to an event.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {Button b;TextBox t;public static void Main() {Application.Run(new zzz());}zzz() {b = new Button();b.Location = new Point(100,200);b.Size = new Size(100,50);b.Text = "Vijay Mukhi is smart";b.Click += new System.EventHandler(abc);t = new TextBox();t.Text = "Hell";t.Location = new Point(10,20);Controls.Add(b);Controls.Add(t);}public void abc(object s, System.EventArgs e){MessageBox.Show(t.Text + " " + ClientSize );}}

Screen 4.9 In the Forms Window, we now see two controls: a Button and a TextBox object that lets us enter some text. The textbox widget also has a large number of properties associated with it. We shall not be repeating this obvious fact for all the other controls. The properties Location and Size work in a similar manner when used with any Control, but the property Text differs, depending upon the object in use. For a button, it represents the caption, whereas for a text box, it represents the text that is entered. Thus, some of the properties play different roles when used in different controls.

Page 68: C#   classes

Screen 4.10 Each time the button is clicked, we would like to display the text that has been entered by the user in the text box. In the eventhandler function abc, the property Text reveals the text entered into the textbox. The MessageBox class is used to display the value, along with the size of the client area. You can change the size of the window or change the contents of the text box and observe the contents of the MessageBox changing dynamically.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}public override void Dispose(){base.Dispose();MessageBox.Show( "hi " + ClientSize );}}

This program displays our ability to invoke code at a specific point in time, which in this case, is at the stage when the user closes the window or when the application quits out. It is akin to fulfilling the last wishes of the program.

Screen 4.11 As the application is quitting, it calls a function called Dispose. So, if you ever want code to be called at the point when an application is about to quit out, you must place it in the Dispose function. This code could be used to close files or do anything else that the programmer desires. It is not mandatory to call Dispose of the base class, but it is always a good programming practice to call the base class function first, and then augment it with your own code. In this particular case, it is inconsequential, but under different circumstances, things may go out of hand if this advice is not heeded.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}Brush b; int ii = 0;protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;b = new SolidBrush(Color.Blue);ii++;g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);}

Page 69: C#   classes

} In this program, we are overriding a function called OnPaint, which is present in the Form class. The OnPaint function gets called each time the window has to be redrawn. Therefore, all code that is to be written to the screen must be written in this function. This code cannot be placed anywhere else in the program. Our next endeavor is to create an object that has functions which display text on the screen or draw a picture. The class that contains these display functions is called Graphics. Thus, we create an object g that looks like Graphics. As an object that looks like Graphics cannot be instantiated, WinForms provides us with an object of type PaintEventArgs with the OnPaint Function. This class contains members required for graphical display. Hence, g is initialized to the Graphics member in e. As mentioned earlier, OnPaint gets called whenever our window has to be redrawn. Whenever OnPaint gets called, it creates an object that looks like PaintEventArgs and then passes it as a parameter to the function. This object has a member called Graphics, which contains functions used for drawing in our client area. The DrawString function requires the text that is to be displayed and its Font. The Form class provides us with the object called Font. Thereafter, the text color, or to be more precise, the brush is to be specified. Here, we want a solid Brush like object. So, we create an object b, and give it a color in which the text should be displayed. There is a static object Blue in the class Color that stands for the color blue. The spelling of 'color' is as per the American usage. Finally, the x and y co-ordinates on the screen are specified. This positions the text in the window at these specified co-ordinates. Thus, the function has a total of 5 parameters:-

• The text to be displayed. • The font in which the text is to be displayed. • The text color or the brush.• The x co-ordinate. • The y co-ordinate.

Here, we have specified certain values, but every time we use DrawString, we can conveniently specify different values for these parameters. Thus, the second DrawString function can display different text and use a different font or brush. As the system does not have a default brush or font, we call it a Stateless Model.

Screen 4.12 Along with 'Vijay Mukhi', we have used a variable called ii, which has been initialized to 0. In the OnPaint function, we increment this variable by 1. Before the window is displayed, function OnPaint gets called. Thereafter, OnPaint gets called whenever the 'minimize' and 'maximize' buttons of the window are clicked. The function OnPaint gets called whenever our client area has to be redrawn due to any action carried out by the user. This function has to be marked with the modifier named protected. This is because the original function in the Form class is tagged with this modifier. We can override a function of the base class, provided we do not change any of the modifiers. By making OnPaint protected, only derived classes can use the OnPaint function.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}Brush b; int ii = 0;protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;

Page 70: C#   classes

b = new SolidBrush(Color.Blue);ii++;g.DrawString("Vijay Mukhi " + ii, Font, b ,1,25);RectangleF r = new RectangleF(20, 60, 100, 25); g.FillRectangle(new SolidBrush(Color.Gainsboro), r);g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Red), r);StringFormat f = new StringFormat();f.Alignment=StringAlignment.Center;RectangleF r1 = new RectangleF(20, 100, 100, 25); g.DrawString("Sonal Mukhi", Font, new SolidBrush(Color.Black), r1,f);g.RotateTransform(-30);g.TranslateTransform(0, 100);g.DrawString("vijay mukhi", Font, new SolidBrush(Color.Orange), 20, 40);g.ResetTransform();}}

Screen 4.13 The output of this program is a window with text displayed haphazardly. This output is nothing to write home about, but is useful in elucidating numerous concepts. A rectangleF structure stores two point objects i.e. it specifies a rectangular area of the window. We start at one corner, where the x and y co-ordinates are 20 and 60, and the opposite corner where the x and y co-ordinates are 100 and 25 respectively. The function FillRectangle from the Graphics class is used to create and fill the above rectangular portion of the screen with the color Gainsboro. The DrawString function is overloaded to take not only x and y as the last two parameters, but also a rectangular area into which it will draw a string. We would now like to center the above string in the rectangular area. This is easier said than done, because, it entails creation of an object that looks like StringFormat with the Alignment property set as Center. The documentation specifies many more options that can be implemented. The StringFormat object is passed as the last parameter to the DrawString function, resulting in the string being shown as centered, instead of being Left aligned, which is the default setting. If we want to rotate the image by 30 degrees, we just have to call a function named RotateTransform from the Graphics class and pass as a parameter, the amount of rotation that is required. You can then watch the image get displayed at the specified angle. Beware, too acute an angle may sprain your neck! The next function, named TranslateTransform, is optional. It is used to move the text around in the client area horizontally or vertically. Whenever we transform something, it stays in the transformed position. But thereafter, if we do not want the other objects to be in this form, we need to use the function ResetTranform to undo the transform. However, it is optional.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}Brush b; protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;b = new SolidBrush(Color.FromArgb(180, Color.Black));RectangleF r = new RectangleF(20, 20, 50, 50); g.FillRectangle(b, r);}

Page 71: C#   classes

}

Screen 4.14 The topic of Brushes is so exhaustive that a thesis can well be written on it. In this program, we use a special brush to fill up a rectangular area on our screen. Here, we specify not only a color, but also a number, which is the alpha value and has a range from 0 to 255. The larger the value, the darker will be the color. To put it technically, the larger the value, the lesser will be the translucence and vice-versa.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}Brush b; protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;b = new SolidBrush(Color.Black);Font f = new Font("Times New Roman", 30);g.DrawString("Vijay Mukhi " , f , b ,1,25);}}

Screen 4.15 In this program, we will shed light on Fonts. When you read a newspaper or magazine, the style of the letters looks different in each of them. This difference is due to the Font or the Typeface used. There are numerous fonts in the world of letters. While displaying text, we can be very specific about the way in which the letters look. To enhance their visual appeal, we create an object that looks like Font. Then, in the constructor, the Name of the font is specified along with the Size in points. Remember that 72 points make an inch. Thus my name, Vijay Mukhi, now gets displayed in a size that is bigger than normal.

a.csusing System.Windows.Forms;

Page 72: C#   classes

using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;Image i;i = new Bitmap("sample.jpg");g.DrawImage(i, 29, 20, 283, 212);}}

Screen 4.16The above program merely displays an image. A file with a jpg or a gif extension contains images or pictures. To display images, we use a class called Image that can recognize pictures. Even though i is an image object, we initialize it to an object that looks like Bitmap. An Image class is an abstract class and the class Bitmap derives from it. An Image class could represent a picture, which is not just an image, but could also be a cursor, icon etc. The DrawImage function accepts an image object as the first parameter, followed by the screen co-ordinates at which the image has to be positioned. The above .jpg file is part of the samples offered while installing the .NET SDK. So, search for the file and copy it to the current working directory. Like the text sample, this picture can also be rotated, transformed etc.

a.csusing System.Windows.Forms;using System.Drawing;public class zzz: Form {public static void Main(){Application.Run(new zzz());}protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;Image i = new Bitmap("colorbars.jpg");Brush b = new TextureBrush(i);g.DrawString("Vijay Mukhi is very smart" , Font, b ,1,25);}}

Page 73: C#   classes

Screen 4.17 By combining a Brush and an image, we can create a multicolor brush. In one of the earlier programs, we had used a Solid brush. Here, we are using a Texture brush. This brush fills the interiors of a shape with a picture. Thus, the text gets reflected in a brush, which reminds us of a rainbow. You can enhance the aesthetic appeal of your applications by using this facility.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;Pen p = new Pen(Color.FromArgb(150, Color.Purple), 20);p.DashStyle = DashStyle.Dash;p.StartCap = LineCap.Round;Point [] pp = new Point[] {new Point(200, 140),new Point(700, 240),new Point(500, 340)};g.DrawCurve(p,pp);}}

Screen 4.18 The above program introduces freehand drawing. A pen is like an artist's brush, which is used to draw any shape that permeates the mind. In our program, we commence by creating a Pen object p. It is initialized to a particular alpha color using FromArgb function from the Color class, and to a specified width. The constructor can also be provided with other parameters, such as a brush. A pen is used to draw lines and curves. A Pen can also draw a line of a specified width and style. The default DashStyle is Continuous. If we change the DashStyle to Dash, the starting point becomes a rounded edge. The default is a Straight Edge. The line drawn by a pen is very versatile, and can employ a variety of fill styles, colors and textures. The DrawCurve function paints a pen object that specifies how to draw a curve. It has an array of points with the individual three point objects specifying where the curved line should be drawn.

a.csusing System.Windows.Forms;

Page 74: C#   classes

using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main() {Application.Run(new zzz());}protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;Image i= new Bitmap("BoilingPoint.jpg");Brush pb = new TextureBrush(i);Pen p= new Pen(pb, 75);g.DrawLine(p,1,5,150,200);}}

Screen 4.19 We can use a brush that looks like an image and create a pen that will draw lines in the garb of a picture. The DrawLine function accepts two sets of numbers, the x-y co-ordinates of the starting point and the x-y co-ordinates of the ending point. It draws a thick line joining these two points. Thus, we can use this function to draw any possible shape.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}protected override void OnPaint(PaintEventArgs e){Graphics g = e.Graphics;HatchBrush b = new HatchBrush(HatchStyle.ForwardDiagonal, Color.Green, Color.FromArgb(100, Color.Yellow));g.FillEllipse(b, 250, 10, 100, 100);Rectangle r = new Rectangle(300, 250, 100, 100);LinearGradientBrush lb = new LinearGradientBrush(r, Color.Red, Color.Yellow,LinearGradientMode.BackwardDiagonal);g.FillRectangle(lb, r);}}

Page 75: C#   classes

Screen 4.20 On maximizing the screen, we see two figures; one is a filled circle, while the other is a rectangular block. We are equipped with a large number of brushes akin to those in the artistic world. One of them is a HatchBrush. The constructor of HatchBrush accepts a hatch style and two colors, viz. a background color and a foreground color. The first parameter is the hatch style, which can be one of six possible hatch styles. The foreground color, in this case, green, defines the color of the lines to be drawn and the background color defines the color for the gaps between the lines. The FillEllipse function in Graphics fills up the shape to display the effect of the brush. We could have used the Rectangle function also, but as we are trying to be as akin as possible to the samples provided by Microsoft, we have used the Ellipse function. A LinearGradientBrush can represent color gradients and multi-color gradients. A gradient represents a transformation from one color to another. A linear gradient is defined alongside a line that is specified by the width of a rectangle or by any two points. Thus, a two-color gradient will commence with a starting color and conclude with the ending color. The blend from one color to the next can be customized. First, we specify the object that is to be colored, which is a rectangle in this case. The gradient starts with the left corner and ends at the lower right corner. Thereafter, we follow with the starting color followed by the ending color. Finally, the angle measured in degrees in the clockwise direction is mentioned, starting from the x-axis. This defines the orientation of the gradient. You can change the angle and witness the spectacular effects. Menus

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}MainMenu m;public zzz(){m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");Menu = m;}}

Page 76: C#   classes

Screen 4.21 Let us now build a menu. We have created an object m, which symbolizes the MainMenu. MainMenu is called a control and represents the menu structure for a Form. It is the root of the menu. A menu consists of various menu items, which are displayed horizontally across the menu. We want to create a menu item that displays the word 'File For this, we need another class called MenuItem. A MenuItem can represent either an individual menu item depicting a command, or it can cascade to another popup of menu items. MenuItems is a read-only property in MainMenu that gives a reference to all the MenuItems currently available in the menu. We have none so far. This CollectionObject also has a function called Add, which is used to add menu items. To do so, the text of the item that is to be displayed must be stated as the parameter to the Add function. We can also remove any menu item that has been previously added. The variable mi stores the MenuItem object returned by the Add function. Thereafter, Menu, which is an object of type MainMenu, available in Form, is initialized to the menu that we desire. The appearance of the menu depends upon the menu object stored in Menu. When we run the program, we see the word File displayed in the top left corner. At this stage, nothing happens when we click on it. On pressing the Alt key, F is displayed as underlined since the symbol & underlines the character it is preceded with.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main() {Application.Run(new zzz());}MainMenu m;public zzz() {m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");mi.MenuItems.Add("Hi");mi.MenuItems.Add("-");mi.MenuItems.Add("Bye");Menu = m;} }

Screen 4.22

Page 77: C#   classes

Now things look more visually attractive. When we click on File or use the accelerator Alt-F, a menu pops up with the word 'Hi', followed by a separator and then finally by the word 'Bye'. A separator is used to logically group menus together. However, when we click on 'hi' or 'bye', nothing happens. This situation needs to be redressed, since a menu should activate some code.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}MainMenu m;public zzz(){m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");MenuItem m1;m1 = new MenuItem("Hi", new System.EventHandler(abc), Shortcut.CtrlF11);mi.MenuItems.Add(m1);mi.MenuItems.Add("Bye");Menu = m;}void abc(object sender, System.EventArgs e) {MessageBox.Show("hell");}}

Screen 4.24

Screen 4.23 Now, whether you either click on File and then on the word 'Hi', or you press Control+F11, you will see a message box with the word "hell" displayed in it. The MenuItem constructor is overloaded. The first parameter is the text to be displayed. The second parameter is a delegate that encompasses the function to be called whenever this menu item is activated. Just as the pen is mightier than the sword, under some circumstances, the keyboard is certainly mightier than the mouse. At times, it is faster to use a keyboard shortcut, instead of using the mouse. Thus, the last parameter is the keyboard shortcut key, which is part of an enumerator. This MenuItem object is passed to the Add function, that either accepts a string or a MenuItem object.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());

Page 78: C#   classes

}MainMenu m;public zzz(){m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");MenuItem a = new MenuItem("One",new System.EventHandler(abc));MenuItem b = new MenuItem("two",new System.EventHandler(abc));mi.MenuItems.Add("hell",(new MenuItem[]{ a, b }));Menu = m;}void abc(object sender, System.EventArgs e) {MessageBox.Show("hell");}}

Screen 4.26

Screen 4.25 Here, we have a popup within a popup. When you click on File, you will see the word 'hell' displayed. You will also see an arrow pointing to the right, along with the menu item. If you move the mouse over the arrow, a popup is displayed, containing the two menu items 'one' and 'two'. If we click on them, a message box with the word 'hell' gets displayed. In the program, with a single statement, we have created two menu items, a and b, followed by an array of menu items. This array is then passed as the last parameter to the Add function. Thus, all the menus become sub-menus. In this case, the event handler is associated with the submenu options, since clicking on the menu item displays the sub-menu.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}MainMenu m;public zzz(){m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");mi.MenuItems.Add( "hell",new System.EventHandler(abc));mi.MenuItems.Add( "Bye",new System.EventHandler(abc));Menu = m;}void abc(object s, System.EventArgs e) {MenuItem m = (MenuItem) s;if ( m.Checked)

Page 79: C#   classes

m.Checked = false;elsem.Checked = true;}}

Screen 4.27 Screen 4.28 We add two menu items, 'hell' and 'Bye' to our File menu and assign the same function abc to handle their events. Clicking on any one of the menu options results in a call to the function abc. This function takes two parameters. The first parameter s, represents the menu item that was clicked on. If the first menu option 'hell' is clicked, then the parameter s is not an object, but a menu item representing 'hell' and vice versa. Every MenuItem has an option called Checked, which if True, will display a tick mark on the menu item. Thus, you can Check or Uncheck a menu option by clicking on it. You may click on each menu option to observe this effect.

a.csusing System.Windows.Forms;using System.Drawing;using System.Drawing.Drawing2D;public class zzz: Form {public static void Main(){Application.Run(new zzz());}MainMenu m;public zzz(){m = new MainMenu();MenuItem mi= m.MenuItems.Add("&File");mi.MenuItems.Add( "hell",new System.EventHandler(abc));mi.MenuItems.Add( "Bye",new System.EventHandler(abc));Label l = new Label();ContextMenu lm;lm = new ContextMenu();l.ContextMenu = lm;l.Text = "Vijay Mukhi";lm.MenuItems.Add(mi.CloneMenu());Controls.Add(l );Menu = m;}void abc(object s, System.EventArgs e) {MenuItem m = (MenuItem) s ;if ( m.Checked)m.Checked = false;elsem.Checked = true;}}

Page 80: C#   classes

The program output will display the same menu - File, as seen in the earlier program. The text Vijay Mukhi will also be visible. If you place the mouse on this text and right click the mouse, you will see the same menu as seen with the File option.

Screen 4.29 Screen 4.30 This is called a Context Sensitive Menu. These two menus, however, are different. If you Check a menu option in this menu by clicking on it, it does not carry the tick mark to the other menu. We first create two objects:

• The first is lm, which looks like a ContextMenu. • The second is l, which looks like a label.

Screen 4.31 Every label has a member called ContextMenu, wherein we can specify a Context Sensitive menu. This member is initialized to lm. As we have already created a menu item mi, we can reuse this menu item. However, a menu item cannot be used twice. Hence, calling the function CloneMenu off MenuItem creates a clone. This clone menu is then passed to the Add function of MenuItems in the ContextMenu. Writing Controls Let us start by creating the simplest control that money can buy. We create the following files:

c.csusing System.Windows.Forms;using System.Drawing;public class yyy : Control{} h.csusing System.Windows.Forms;public class zzz : System.Windows.Forms.Form{yyy a;

Page 81: C#   classes

public zzz(){a = new yyy();Controls.Add(a);}public static void Main() {Application.Run(new zzz());}}

a.batdel *.exedel *.dllcsc.exe /t:library c.cscsc.exe /r:c.dll h.csh

The file c.cs contains our very first custom control. In order to create our own user-defined control, we create a class yyy and derive it from the Control class. The Control class implements the basic code required by classes to implement the behavior of a control or a widget. This code can handle user input with a keyboard or a pointing device, such as a mouse. Message handling and security features are also supported.At the end of the day, all controls are merely child windows. The Control class defines the area or bounds of a control along with the fonts, colors and images. This class allows painting, context menus and anchoring with docking behavior. Earlier, we had displayed scores of controls in our containers, written by the Microsoft developers. All these controls were derived from the Control class.

Screen 4.32 Thus, a user control like yyy is an instance of a Control class, which is added to the Form using the Add function off the Controls collection. It can't get any simpler. On running the executable, we see no output. Yet, since no error was generated, we presume that all went well. The major difference between the Microsoft controls and our controls is, the file in which the code for the control is finally placed. We have placed our control code in assembly c.dll, whereas, Microsoft controls are placed in System.Windows.Forms.dll.

c.cs using System.Windows.Forms;using System.Drawing;public class yyy : Control {protected override void OnPaint(PaintEventArgs e){e.Graphics.DrawString(Text,Font, new SolidBrush(ForeColor), ClientRectangle);}}

h.csusing System.Windows.Forms;public class zzz : System.Windows.Forms.Form{yyy a;public zzz() {a = new yyy();a.Size = new System.Drawing.Size(600, 450);

Page 82: C#   classes

a.Text = "Vijay Mukhi";Controls.Add(a);}public static void Main() {Application.Run(new zzz());}}

Screen 4.33 In the above example, we have overridden a function called OnPaint in the Control class. This function gets called whenever a control is to be redrawn on the screen. It is passed a PaintEventArgs object as a parameter, from where we summon the DrawString function to paint a string in a specified font and color, at a particular location. The first parameter, Text, is a property, which refers to the string to be displayed. The string 'Vijay Mukhi' is presently displayed in the window. In the container h.cs, we have initialized the property Text contained in the Control class to 'Vijay Mukhi'. The Size property is also initialized, so that our control has a specific size in the container.

h.csusing System.Drawing;using System.Windows.Forms;public class zzz : Form {Button b;ccc c;public zzz() {b = new Button();c = new ccc();b.Anchor = System.Windows.Forms.AnchorStyles.Bottom;b.DialogResult = System.Windows.Forms.DialogResult.OK;b.FlatStyle = System.Windows.Forms.FlatStyle.Flat;b.Size = new System.Drawing.Size(96, 24);b.Text = "&Save";b.Location = new System.Drawing.Point(8, 328);b.Click += new System.EventHandler(abc);Text = "Sonal Mukhi";AcceptButton = b;ClientSize = new System.Drawing.Size(400, 373);c.Anchor=AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;c.AutoScrollMinSize = new System.Drawing.Size(0, 0);c.Size = new System.Drawing.Size(400, 310);c.Text = "Vijay Mukhi";Controls.Add(b);Controls.Add(c);c.cust = ddd.rrr();Size = new Size(400, (373 + SystemInformation.CaptionHeight));}void abc(object sender, System.EventArgs e) {c.aaa();MessageBox.Show("vijay "+ c.cust);}public static void Main(string[] args){Application.Run(new zzz());}

Page 83: C#   classes

}c.csusing System;using System.Windows.Forms;using System.Drawing;public class ccc : UserControl{TextBox t;TextBox ID;Label l;ddd c;public ccc(){t = new TextBox();l = new Label();ID = new TextBox();Text = "Vijay Mukhi";Size = new System.Drawing.Size(384, 304);t.Size = new System.Drawing.Size(88, 20);t.Location = new System.Drawing.Point(88, 70);l.Size = new System.Drawing.Size(64, 16);l.Location = new System.Drawing.Point(8, 32);l.Text = "ID:";ID.ReadOnly = true;ID.Size = new System.Drawing.Size(200, 20);ID.Location = new System.Drawing.Point(88, 30);ID.Enabled = false;Controls.Add(t);Controls.Add(ID);Controls.Add(l);}public ddd cust{get{return c;}set{c=value;ID.Text = c.ID;t.Text = c.ti;}}public void aaa(){c.ti = t.Text;}}

cc.csusing System;using System.ComponentModel;using System.IO;public class ddd : Component{string i ;string t ;public static ddd rrr(){ddd c = new ddd("111");c.ti = "Vijay";return c;}internal ddd(string s): base(){i = s ; }public string ID{get{return i ;

Page 84: C#   classes

}}public string ti{get{return t ;}set{t = value ;}}public override string ToString(){StringWriter sb = new StringWriter() ;sb.WriteLine("Sonal \n");sb.WriteLine(i);sb.Write(t);return sb.ToString();}}

a.batdel *.exedel *.dllcsc.exe /t:library /out:c.dll c.cs cc.cs csc.exe /R:c.dll h.csh

This program is a rather protracted one. As usual, we start with the container in h.cs. In the zzz constructor, we first create a button b and an object c that is an instance of our user control ccc. The control is present in the assembly c.dll. What this class presently does is not significant. We begin by initializing a large number of properties in the button. The Anchor property decides as to which edges of the control are to be anchored with the edges of the container. Here, we have chosen the Bottom edge. The DialogResult property is the value that is returned to the parent form when we click on the button. The value returned is OK. The FlatStyle property belongs to the ButtonBase class and is one of the numerous properties that influence the flat style appearance of the button. Knowledge of GUI programming implies cognizance and comprehension about all the facets of making your application more comely and pleasing to the eye. The Size, Text and Location properties were explained earlier. Each time we click on the button, the function abc gets called. The Text property decides on the title of the windows. The AcceptButton property requires an object that represents a button. Every form has a feature, which associates the Enter key with a button. The resultant effect is that pressing the Enter key on the keyboard simulates a click on the associated button. Thus, in the above form, pressing Enter or clicking on the button would result in a call to the function abc. The ClientSize property decides the size of the windows. Our User Control too can initialize properties since they belong to the Control class. In the program, we have set the Anchor, AutoScrollMinSize, Size and Text properties of our user-defined control class ccc, even though our control may not have implemented these properties directly. Using the Add function, we have then added the button and the control ccc to the form. Finally, we have called a static function rrr from the class ddd that initializes a property cust from our user-defined control. When we run this program, we see two text boxes, a label and a button. It is obvious that other than the button, the other widgets were created by the class ccc. This provides ample credence to our belief that our user-defined control can do its biding.

Page 85: C#   classes

Screen 4.35

Screen 4.34 We shall now endeavor to comprehend what the constructor of class ccc in file c.cs is attempting to do. The constructor contains two text boxes, one called ID to store the id of the user, and the other called t to store the user's name. The label l is used to display a simple descriptive message. Since we do not want the user to change the value contained in ID, we assign the value true to its ReadOnly property and assign the value false to its Enabled property. Thereafter, we add these three widgets to the form. Hence, we can now see four widgets on the screen. In the container h.cs, we call a static function, rrr off class ddd. The ddd class is created in cc.cs and is derived from Component. In the rrr function, we create an object c, which looks like ddd and pass a value of 111 to the constructor. The constructor of class ddd initializes an instance variable i to the value contained in s. The variable i stands for the user id. Class ddd has a property called ti, which is initialized to my name, Vijay. This property ti sets another instance variable t to the value 'Vijay'. Thus, we have initialized two instance members of class ddd to specific values. The value returned on calling the rrr function is stored in the cust property of the control c. The class ccc contains the property having type ddd. The property Cust stores the ddd object in the variable c for later use. It also initializes the Text property of the text boxes to the ID and Name of the user that the two properties in the class ddd were initialized to. Thus, we see '111' and 'Vijay' displayed in the text boxes. When we click on the button labeled 'Save', the function abc gets called. This function first calls the function aaa from class ccc using the object c. In aaa, we initialize the ti property of the control to the value present in the textbox. The ID property is dimmed out, and hence its value can never be changed. The object c represents the ddd object in class ccc. To display a string, an object, whose data type is not a string, has to call the ToString function in the datatype. The cust property in class ccc has the type of ddd, which contains the ToString function. This function uses the StringWriter class to concatenate the word 'Sonal' with the value of the instance variables i and t, which eventually get displayed in the MessageBox. The above program demonstrates two crucial points:

(a) All the code that refers to the user has been encapsulated in class ddd. (b) The user interface code is entered in class ccc.

The container is oblivious to these classes and does not bother to verify whether there are two classes or one. While the class ccc contains code pertaining to User Interface interaction only, the class ddd contains code relating to the actual object.

h.csusing System.Drawing;using System.Windows.Forms;public class zzz : Form {RadioButton r1,r2;GroupBox g1;sss s;public zzz(){r1 = new System.Windows.Forms.RadioButton();r2 = new System.Windows.Forms.RadioButton();r1.Location = new System.Drawing.Point(24, 24);r1.Size = new System.Drawing.Size(128, 24);r1.Text = "Vijay";r1.Checked = true;r1.CheckedChanged += new System.EventHandler(r1f);r2.Location = new System.Drawing.Point(24, 64);r2.Size = new System.Drawing.Size(128, 24);

Page 86: C#   classes

r2.Text = "Mukhi";r2.CheckedChanged += new System.EventHandler(r2f);g1 = new System.Windows.Forms.GroupBox();g1.Size = new System.Drawing.Size(192, 152);g1.Text = "Sonal";g1.Location = new System.Drawing.Point(320, 16);s = new sss();Text = "Control Example";ClientSize = new System.Drawing.Size(528, 325);s.Size = new System.Drawing.Size(304, 328);s.TabIndex = 0;s.Anchor = AnchorStyles.Left | AnchorStyles.Right;s.Font = new System.Drawing.Font("TAHOMA", 16f, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.World);s.Text = "Simple Control";s.dmc += new System.EventHandler(sf);Controls.Add(g1);Controls.Add(s);g1.Controls.Add(r2);g1.Controls.Add(r1);}void r2f(object sender, System.EventArgs e){if (r2.Checked){s.dm = ddd.a2;}}void r1f(object sender, System.EventArgs e){if (r1.Checked){s.dm = ddd.a1;}}void sf(object sender, System.EventArgs e){if (s.dm == ddd.a1)MessageBox.Show("hi");if (s.dm == ddd.a2)MessageBox.Show("bye");}public static void Main(){Application.Run(new zzz());}}

c.csusing System;using System.ComponentModel;using System.Windows.Forms;using System.Drawing;[DefaultProperty("dm"),DefaultEvent("dmc"),]public class sss : Control{ddd d;EventHandler eee; public sss() :base(){d = ddd.a1;ccc();SetStyle(ControlStyles.ResizeRedraw, true);}[Category("Appearance"),Description("Controls how the control paints"),DefaultValue(ddd.a1),Bindable(true),]public ddd dm{get{return d;}set{

Page 87: C#   classes

d=value;ccc();dmf(EventArgs.Empty);}}protected override void OnPaint(PaintEventArgs e){e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle);Size textSize = e.Graphics.MeasureString(Text, Font).ToSize();float x = (ClientRectangle.Width/2) - (textSize.Width/2);float y = (ClientRectangle.Height/2) - (textSize.Height/2);e.Graphics.DrawString(Text,Font,new SolidBrush(ForeColor),x, y);}protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); Invalidate();}[Description("Raised when the DrawingMode changes")]public event EventHandler dmc{add { eee += value; }remove { eee -= value; }}protected virtual void dmf(EventArgs e){Invalidate();if (eee != null)eee.Invoke(this, e);}void ccc(){if ( d == ddd.a1){base.BackColor = Color.Yellow ;base.ForeColor = Color.Green ;}if ( d == ddd.a2){base.BackColor = Color.LightSlateGray ;base.ForeColor = Color.White ;}}}public enum ddd{a1 = 0,a2 = 1,}

a.batdel *.exedel *.dllcsc /t:library c.cscsc h.cs /r:c.dllh

Let us now write a control that is considerably intricate. In the container h.cs, we start with two radio buttons r1 and r2. Each time we select a radio button, depending upon the option selected, either of the functions r1f or r2f will get called. Thereafter, we create a group box called g1. The radio buttons are added to the group box, and the group box is added to the Controls collections. Apart from these controls, one more user control named s, which is an instance of class sss, is added to the Controls collection.

Page 88: C#   classes

Screen 4.36 Screen 4.37 Prior to this, we initialize various properties of this control such as TabIndex, Font, Size, Anchor, Text etc. to some meaningful values. Besides these, a property called dmc in the user control is initialized to an EventHandler that calls function sf. Thus, whenever the event represented by dmc is triggered, the function sf gets called. The code implementing our user control s, resides in the file c.cs. The user control s is an instance of sss and is derived from the Control class. At the outset, the constructor of class sss calls the constructor of the base class using the keyword base, even though this is optional, because the base class constructor invariably gets called. The class ddd is an enum, with two members a1 and a2, having values 0 and 1 respectively. We could conveniently have used numbers directly instead of an enum, but since the original example used an enum, we have also done so. We set the object d to the value 0 and call function ccc from class sss. The main objective of placing code in a function is to enable the code to be called several times. In the function ccc, we start by checking the value of the object d. If it is a1 i.e. 0, we change the value of the two properties BackColor and ForeColor to Yellow and Green respectively. If the object has a value of a2, then another pair of colors is assigned to these properties. The properties are changed in the base class using the keyword base. The function SetStyle ensures that the form gets redrawn when it is resized. We have already learnt that the OnPaint function is called whenever the window needs to be redrawn. In this function, we first use the property BackColor to fill the form background. Next, we use the width of the currently selected font, to calculate the midpoint of our screen, and then, we write the value contained in the text property in the center of the window. When the second radio button is selected, function r2f gets called. In this function, the program checks whether the radio button is already checked. If so, it initializes the property dm, whose data type is ddd, to a2. Similarly, when the first radio button is selected, function r1f gets called. This function first ascertains if the radio button is already checked. If so, it initializes the property dm to a1. Now, we shall focus our attention on the property dm. In the set accessor of property dm, the ddd object named d is initialized to either a1 or a2. Following this action, a call is made to function ccc, which changes the background and foreground color, depending on the value contained in d. The effect is observed when the function OnPaint gets called. A call is made to the function dmf with a parameter of an Empty event. In function dmf, we first call Invalidate, which in turn, calls the OnPaint function. Just as life offers no guarantees whatsoever, in much the same way, the calls made to the OnPaint function are unpredictable. The Invalidate function instantly calls the OnPaint function. You may recall that in h.cs, the property dmc was initialized with the name of the function sf. This property dmc is an event type that stores the EventHandler or function sf in an instance object eee. So, the value in the object eee is checked. If the value is not null, the function Invoke is called off the object eee with two parameters. The first parameter is a reference to itself, i.e. 'this', and the second parameter is a null EventArgs object. The function Invoke, in turn, calls function sf in the container, h.cs. The function sf displays a message box, depending upon the value of the ddd object. The main idea behind this exercise is to demonstrate that clicking on a radio button in the container initializes a property in the user control. This in turn, raises a property changed event, thus resulting in a call to a function registered with a property of the control. The function resides in the container and not in the user control. This is a circuitous route for accomplishing results. The Invoke function is not aware of and could not care less about the functions that it is calling. All the other attributes in the code can be safely ignored, since they are mainly meant for external tools or programs that display the metadata.

h.csusing System.Drawing;using System.Windows.Forms;public class zzz : Form{

Page 89: C#   classes

TextBox t;Button b;hhh h;public zzz(){h = new hhh();b = new Button();t = new TextBox();b.Size = new System.Drawing.Size(104, 40);b.Text = "Vijay";b.Location = new System.Drawing.Point(336, 56);ClientSize = new System.Drawing.Size(448, 157);t.Location = new System.Drawing.Point(80, 16);t.Text = "Vijay Mukhi";h.Dock = System.Windows.Forms.DockStyle.Bottom;h.Size = new System.Drawing.Size(448, 40);h.Location = new System.Drawing.Point(0, 117);h.Text = "none";h.ppp(t, "TextBox selected");h.ppp(b, "Button Selected");Controls.Add(t);Controls.Add(b);Controls.Add(h);}public static void Main(string[] args){Application.Run(new zzz());}} c.csusing System;using System.Collections;using System.ComponentModel;using System.Drawing;using System.Windows.Forms;public class hhh : Control{Hashtable h;Control a;public hhh(){h = new Hashtable();BackColor = SystemColors.Info;}[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ]public override string Text{get{return base.Text;} set{base.Text = value;}}private void ce(object s, EventArgs e){a = (Control)s;Invalidate();}private void cl(object s, EventArgs e){if (s == a){a = null;Invalidate();}}public void ppp(Control c, string v){if (v == null)

Page 90: C#   classes

{v = string.Empty;}if (v.Length == 0){h.Remove(c);c.Enter -= new EventHandler(ce);c.Leave -= new EventHandler(cl);}else{h[c] = v;c.Enter += new EventHandler(ce);c.Leave += new EventHandler(cl);}if (c == a){Invalidate();}}protected override void OnPaint(PaintEventArgs pe){base.OnPaint(pe);Rectangle rect = ClientRectangle;Pen borderPen = new Pen(ForeColor);pe.Graphics.DrawRectangle(borderPen, rect);borderPen.Dispose();if (a != null){string te = (string)h[a];if (te != null && te.Length > 0){rect.Inflate(-2, -2);Brush brush = new SolidBrush(ForeColor);pe.Graphics.DrawString(te, Font, brush, rect);brush.Dispose();}}}}

In this program, we have three controls: a user control h, contained in class hhh, a TextBox t and a Button b. The basic properties like Size, Text and Location for the Button and the Text box are set to the specified initial values. Thereafter, the properties of the user control h are initialized. The DockStyle for the Dock property is set to the Bottom of the form, and the Size and the Location are specified. The Text property is also initialized. The user control h has a property called ppp, which accepts two parameters, a control and a string. We call it twice. When it is called for the first time, the first parameter passed is a text box control. The next time it is called, the first parameter passed is a button control. The button and the text box are displayed on the screen and a yellow colored label with the word 'TextBox selected' in the bottom pane.

Screen 4.38 When we click on the button, the words in the label change to 'Button selected'.

Screen 4.39

Page 91: C#   classes

Thus, depending upon the control selected, our user control displays a help message which we have registered using the property ppp. The user control h in file c.cs, has two instance variables, a HashTable h and a Control a. The HashTable class stores values based on a certain key. This helps in retrieving the value efficiently on being provided with the key. In the constructor of class hhh, an instance h of the HashTable is created. The property BackColor is initialized to a Read Only Color property from the class SystemColors. This property represents the tool tip background color. On our machine, it happens to be Yellow, and thus, we see a yellow color label. There is a property called Text in the Control class. In order to implement our property, we must override the existing one. Presently, in our property, we are merely accessing the original Text in the base class. The attributes make interesting reading even though they have little use in the current example. The Browsable attribute with a parameter of False prevents this property from showing up in the Property Browser. Also, the value assigned to the property is not saved on disk. We are not doing anything useful in the Text property at all. The property ppp is called twice in the control, because the container has two property initialization statements. Good programming style incorporates comprehensive error checks. We first check for a string value in the second parameter v. If it is null, we initialize the variable v to an empty string. If the string is not empty, we add the string contained in v to the hash table using the control parameter c as the key. Thus, in a hash table, any data type can be used as a key to insert or retrieve values. The control class has Events called Enter and Leave. We use the += syntax to register the function ce whenever the Event Enter gets fired. In the same manner, the function cl gets called whenever the Event Leave gets fired. If the user calls the property without a string, it signifies that the control has to be removed from the hash table and the list of functions in the events has to be called. Thus, we use the Remove function of the HashTable class to remove the key c from the hash table. The -= syntax of the Events and Delegates is employed to remove the functions registered with the Enter and Leave events.In the OnPaint function, we have drawn a label and displayed some text in it. There is one rule that must never be violated: 'Call the function in the base class first.' This is because we are unaware of what the overridden function accomplishes. A Pen is created from the color stored in ForeColor. This color is the default color stored in the Control class. The Background color is initialized in the constructor of hhh class. A rectangle that is drawn using this pen displays yellow as the background color, with the size defaulting to the value stored in the ClientRectangle property in the control class. The pen is then disposed off, to enable the system to retrieve the resources consumed by it. A check is thereafter performed on the value in the Control object. It should be null since the control has not been initialized in the beginning. Before the OnPaint function gets called, a lot of activities get executed in the background, i.e. many functions get called and numerous events get triggered. On entering a field, the event OnControlEnter gets triggered and the function that is registered with the Enter event is called. In our case, function ce is called. The first parameter to this function is a handle to the control that caused the event. In our case, it is the Text Box. We initialize the object a to the Text Box control. Thus, in OnPaint, the value of control a is a Text Box. We then retrieve the string stored in the hash table using the control as the key. We then ascertain that the value contained in the string te is not null and its length is greater than zero. The Rect structure contains the size of the rectangle. We can then inflate or deflate the rectangle using the Inflate method. As a result, the position and the size of the Rectangle change. The X and Y properties are changed by the amount specified and the Width and Height are modified to twice the amount specified. The final outcome is that the size of the rectangle is inflated without moving its geometric center. In our case, since the numbers are negative, the rectangular will be deflated. Following this, a solid brush is created using the property ForeColor. A string is drawn using the DrawString function in the Pen class. Thereafter, we dispose of the brush. When we leave the control, the event handler named Leave is called. The function associated with this eventhandler is cl, which ascertains whether the control in 'a' and the one passed as parameter i.e. 's' is the same. If so, then 'a' is initialized to a Null value and the OnPaint function is called. The value contained in 'a' is the deciding factor on whether we are inside a control or not.

h.csusing System.Drawing;using System.Windows.Forms;public class zzz : Form{fff f;public zzz(){f = new fff();f.Dock = System.Windows.Forms.DockStyle.Fill;f.ForeColor = System.Drawing.Color.White;f.BackColor = System.Drawing.Color.Black;f.Size = new System.Drawing.Size(600, 450);

Page 92: C#   classes

f.vvv = 73;f.Text = "Vijay Mukhi";ClientSize = new System.Drawing.Size(600, 450);Controls.Add(f);}public static void Main(){Application.Run(new zzz());}} c.csusing System;using System.Drawing;using System.Drawing.Drawing2D;using System.Windows.Forms;public class fff : Control {int vvv1 = 0;public int Min = 0;public int Max = 100;public bool ShowValue = false;int dv = 0;bool d = false;public Color cs = Color.Red;public Color ce = Color.LimeGreen;Brush bb = null;Brush bd = null;public int vvv{get{if (d){return dv;}return vvv1;}set{vvv1 = value;}}protected override void OnPaint(PaintEventArgs e){base.OnPaint(e);bb=new LinearGradientBrush(new Point(0, 0), new Point(ClientSize.Width, 0),cs,ce);bd = new SolidBrush(Color.FromArgb(200, Color.Black));e.Graphics.FillRectangle(bb, ClientRectangle);Rectangle r = ClientRectangle;float p = ((float)vvv / ((float)Max - (float)Min));int a = (int)(p * (float)r.Width);r.X += a;r.Width -= a;e.Graphics.FillRectangle(bd, r);e.Graphics.Flush();RectangleF r1 = new RectangleF();SizeF ts = e.Graphics.MeasureString(Text, Font);r1.Width = ts.Width;r1.Height = ts.Height;r1.X = (ClientRectangle.Width - r1.Width) / 2;r1.Y = (ClientRectangle.Height - r1.Height) / 2;e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), r1);}protected override void OnMouseDown(MouseEventArgs e){base.OnMouseDown(e);Capture = true;d = true;sss(new Point(e.X, e.Y));}protected override void OnMouseMove(MouseEventArgs e){

Page 93: C#   classes

base.OnMouseMove(e);if ( !d) return;sss(new Point(e.X, e.Y));}protected override void OnMouseUp(MouseEventArgs e){base.OnMouseUp(e);if ( !d) return;Capture = false;d = false;vvv = dv;}void sss(Point l) {Rectangle r = ClientRectangle;float p = (float)l.X / (float)r.Width;int a = (int)(p * (float)(Max - Min));int o = dv;dv = a;float op = ((float)o / ((float)Max - (float)Min));int ol = (int)(op * (float)r.Width);float np = ((float)dv / ((float)Max - (float)Min));int nl = (int)(np * (float)r.Width);int mi = Math.Min(ol, nl);int ma = Math.Max(ol, nl);Rectangle r1 = new Rectangle(r.X + mi, r.Y, ma - mi, r.Height);Invalidate(r1);}}

When we run the above program, we see a myriad of colors such as red, green and black. We also see 'Vijay Mukhi' displayed in the center. On clicking the mouse, more of the color black will be introduced upto the point of the mouse click on the screen, whereas dragging the mouse will extend or diminish the colors accordingly. This certainly is a sight for sore eyes!

Screen 4.40 Screen 4.41 We have to admit that the folks at Microsoft who worked on this sample did a truly marvelous job. Our task here is to explain the program that they have written, in absolute layman terms. The h.cs file contains code that is already explained in the earlier program. As always, along with the properties that are present in Control, we have also initialized one of our own properties, vvv for control fff. The user-defined property vvv is assigned a value of 73. The value contained in the Text property is displayed at the center of the screen. We now focus the spotlight on the actual code of our control fff that resides in the file c.cs. The container sets the value of the property vvv to 73. Therefore, in the control, the property vvv gets called initially. The set accessor initializes the int variable vvv1 to 73. The function OnPaint is responsible for drawing the rainbow of colors on the screen. In this function, we first call the original OnPaint function. Then, we create two brushes to obtain the desired effect. The first brush is a LinearGradientBrush, which can be accessed using the Brush object bb. The last parameter to the constructor of this brush is the starting color cs, which has been initialized to Red, and an ending color ce that is initialized to a value of LimeGreen. The second brush is a SolidBrush bd, which is created to fill up any shape. The function FromArgb of the Color class creates a new color, where the first parameter is an alpha value 200 and the second parameter is the base color.

Page 94: C#   classes

The Graphics property of PaintEventArgs merely returns a Graphics object when we call function FillRectangle. The two parameters to this function are : a Brush that represents a gradient and the Size of the window whose value is stored in the ClientRectangle property. If we stop at this stage, the red and the green gradients will stand out glaringly. Now, let us draw the black rectangle separating the red and green colors. We create a temporary variable r to store the dimensions of the window.Two instance variables, Max and Min having a value of 100 and 0 respectively, are used to decide the factor by which the property vvv should be divided. Since we need to retrieve the value for the property, the get accessor is called. The value contained in d decides on the value to be assigned to the property. Initially d is false, so the value 73 contained in vvv1 is returned. The float variable p now has a value of .73. The Width of the window in our case is 600 pixels. Thus, the variable 'a' is assigned a value of 438. You can use the WriteLine function to display these values. The Width of the rectangle is reduced by this amount and the X position is shifted by 438 pixels. Next, we draw the black rectangle using the function FillRectangle by providing it with a black brush and the new Rectangle object r. Modifying the value of the property vvv from 73 to 3 will result in displaying the entire screen in black color. Thus, the vvv parameter decides the size of the black rectangle; the smaller its value, the larger will be the size of the rectangle. The Flush function ensures that all pending graphic operations on the stack are executed immediately. If we stop here, the gradient and the rectangle will be displayed, but no text will be displayed in the center of the screen. The Rectangular structure r1 stores the size and location of the rectangular region that our string requires. The MeasureString function takes two parameters:

• The first is a string value that is stored in our property Text.• The second is a Font object.

The default Font property is supplied for the second parameter. The function returns a SizeF object, whose Width and Height determine the display region that the string requires. The width and height of rectangle r1 is initialized to the Width and Height of the string. As the string is to be displayed in the center of the form, objects X and Y, which are the members of rectangle r1, are given the following values:

• The Width of the form = the width of the string divided by 2. • The Height of the form = the height of the string divided by 2.

We then use the DrawString function to display the text. This function takes the following parameters in the specified order:

• A string that is stored in the Text property.• The default font.• The color of the brush that is stored in the property ForeColor.• The region, which is stored in object r1.

We have three mouse events that trigger off three functions. They are as follows:

• OnMouseMove: When we move the mouse. • OnMouseDown: When we click the left mouse button, with the mouse cursor placed on the form.• OnMouseUp: When we release the left mouse button.

If we move the mouse around, nothing happens. This is because the 'if' statement results in a value of true, as the variable d has a value of false. So, the function practically achieves nothing. Clicking the left mouse button toggles the value of the variable d to true. Thus, if the variable d is True, we know that the user has clicked the left mouse button, and when it reverts back to false, we know that the left mouse button has been released. The OnMouseMove function does something constructive and gainful only when the left mouse button is depressed. The Capture property reveals whether the control has been able to capture the mouse or not. This property is optional. When we click within the window, the framework is informed about our victory over the mouse, and the variable d is set to true. The next most pertinent action is to call function sss with a Point object. This object stores the position at which the mouse has been clicked. The MouseEventArgs parameter e, has two members X and Y, which identify the current position of the mouse. The function sss is called yet again when the mouse is moved. The OnMouseUp function changes the value of Capture, and sets the value of the boolean variable d to false. It also initializes the value of a property vvv to dv. The net achievement of the function sss is that, it calls the function OnPaint using the Invalidate function. The OnPaint function does not draw or paint the entire form or window, but only the specific part that has been invalidated. This is done to achieve optimum efficiency, since it is preposterous to waste effort in redrawing areas that have not been invalidated. The Invalidate function is also passed a Rect object as a parameter, which takes a decision on the area of the client rectangle that is to be re-drawn. Function sss is therefore, given a smaller Rect object as a parameter, which informs the OnPaint function about the specific part of the form that should be invalidated. The Point parameter is employed to determine the area of the form, that must be redrawn. The co-ordinates of the entire screen are stored in ClientRectangle. We also calculate a percentage p, depending upon our current position, i.e. l.X, divided by the Width of the screen. Next, we multiply this percentage by 100 (the difference between Max and Min). The value of dv is stored in variable o, since we are initializing dv to 'a' in the next line. Two new percentage values, op and np have been calculated. They are merely the values of o and dv divided by 100. Thereafter, we calculate the minimum and maximum values of ol and nl. The new rectangle created is the Client Rectangle,

Page 95: C#   classes

where the minimum value is added to X, and Y is left untouched. The Width is set to the difference of ma and mi, and the Height is left unaltered. The difference between variables ma and mi is negligible. Correspondingly, the width of the invalidated region too is insignificantly small. Further, mi is used to decide the position at which the X of the black rectangle should commence. Thus, if we replace r1 with ClientRectangle, everything would be hunky dory, but the screen will flicker a great deal, while the mouse is in motion. This is because the same screen has to be displayed when the movement ceases. Thus, to thwart this flicker, we redraw only the specific part of the screen that has been invalidated. ScrollBar Control

a.csusing System.Drawing;using System.Windows.Forms;public class zzz : Form{VScrollBar v;HScrollBar h;PictureBox p; Label l2; Label l1; bool d = false;int ox, oy;float vm;float vp;float hm;float hp;public zzz() {ClientSize = new System.Drawing.Size(520, 277);l1 = new Label();l1.Location = new System.Drawing.Point(408, 160);l2 = new Label();l2.Location = new Point(408, 184);v = new VScrollBar();v.Location = new Point(200, 24);v.Size = new Size(16, 152);v.Scroll += new ScrollEventHandler(vScroll); v.Minimum = -100;h = new HScrollBar();h.Location = new Point(16, 176);h.Size = new Size(184, 16);h.Scroll += new ScrollEventHandler(hScroll);h.Minimum = -100;p = new PictureBox();Bitmap b = new Bitmap("water.bmp");p.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;p.Location = new Point(64, 32);p.Size = new Size(96, 96);p.Image = (Image)b;p.MouseDown += new MouseEventHandler(pDown);p.MouseUp += new MouseEventHandler(pUp);p.MouseMove += new MouseEventHandler(pMove);Controls.Add(l2);Controls.Add(l1);Controls.Add(h);Controls.Add(v);Controls.Add(p);v.LargeChange = 20;h.LargeChange = 20;v.SmallChange = 1;h.SmallChange = 1;vmf();hmf();l1.Text = h.Value.ToString();l2.Text = v.Value.ToString();p.Cursor = Cursors.SizeAll;}void vmf(){float hsb = (float)(v.Height - p.Height);float ticks = (float)(v.Maximum - v.Minimum);vm = hsb / ticks;

Page 96: C#   classes

}void hmf(){float hsb = (float)(h.Width - p.Width) ;float ticks = (float)(h.Maximum - h.Minimum) ;hm = hsb / ticks ;}void vScroll(object sender, ScrollEventArgs e){l2.Text = v.Value.ToString() ;vp = (float)(v.Value- v.Minimum);p.Top = v.Bottom - (int)(vm * vp) - p.Height;}void hScroll(object sender, ScrollEventArgs e){l1.Text = h.Value.ToString() ;hp = (float)(h.Value - h.Minimum);p.Left = h.Right - (int)(hm * hp) - p.Width;}void pDown(object s, MouseEventArgs e){d = true;ox = e.X;oy = e.Y;}void pMove(object s, MouseEventArgs e){if (d){int minY = v.Minimum;int maxY = v.Maximum;int minX = h.Minimum;int maxX = h.Maximum;int value = (int)(v.Value - (e.Y - oy)/vm);if (value < minY){v.Value = minY;}else if (value > maxY - v.LargeChange + 1){v.Value = maxY - v.LargeChange + 1;}else{v.Value = value;}value = (int)(h.Value - (e.X - ox)/hm);if (value < minX){h.Value = minX;}else if (value > maxX - h.LargeChange + 1){h.Value = maxX - h.LargeChange + 1;}else{h.Value = value;}l1.Text = h.Value.ToString() ;l2.Text = v.Value.ToString() ;value = p.Top + (e.Y - oy);if (value < v.Top){value = v.Top;}else if (value > v.Bottom - p.Height){value = v.Bottom - p.Height;}p.Top = value;value = p.Left + (e.X - ox);if (value < h.Left)

Page 97: C#   classes

{value = h.Left;}else if (value > h.Right - p.Width){value = h.Right - p.Width;}p.Left = value;}}void pUp(object s, MouseEventArgs e){d = false;}public static void Main(){Application.Run(new zzz());}}

>csc a.cs

Before running the program, copy the file water.bmp into the current directory. This file is provided along with the samples in the installation program. The singular methodology to comprehend large programs is by first examining their output, because the output will motivate you to grasp the program. We see two scrollbars, one vertical and the other horizontal, with a picture within them. Clicking on the scrollbars will move the picture in the direction of the scrollbar that is clicked. Let us understand how this is achieved.

Screen 4.42 We start by examining the constructor of class zzz. The default client size is modified to the desired size. Then, there are two labels l1 and l2, which display the current values of the two scroll bars. A vertical scrollbar v is an instance of a class VScrollBar, and a horizontal scrollbar h is an instance of a class HScrollBar. As is customary, we provide a Location and a Size to the scrollbar. The Minimum and Maximum properties decide the range of values that the user can select. Most controls such as a Text box or a Combo box already have a scroll bar built into them. Hence, they do not require this control. The Scroll event gets triggered whenever the scroll button moves. The button can be moved either by using a mouse or by the keyboard. Moving the vertical scroll button will call the vScroll function, whereas moving the horizontal scroll button will call the hScroll function. After setting the properties of the Scroll Bar, we create an object p, which is an instance of a PictureBox class. Next, we create a Bitmap object, and in the constructor, we pass the file water.bmp to it. The SizeMode decides on the display of the image in the Picture Box. The enum value of StretchImage stretches the image to fit it into the PictureBox, whereas, the enum value of Normal places it in the upper left corner. The image property is assigned to the picture or the bitmap that is to be displayed. Three event handlers are attached to the image:

• pDown : This is called each time we click in the picture.• pUp : This is called when we release the mouse button. • pMove : This is called when we move the mouse within the picture.

Finally, all the controls i.e. two labels, two scroll bars and one image, are appended to the Control class. The property of LargeChange in the scrollbar control decides on the magnitude of the change in the value of the scroll bar, when the scroll bar is clicked. The SmallChange property is associated with the arrows on the scroll bar. We have set the LargeChange property to 20 and the SmallChange to 1.

Page 98: C#   classes

To dispel the banality of the somber explanation given above, let us digress slightly to present a small, albeit important, elucidation on how Windows handles the art of scrolling.

Screen 4.43 The value of the scrollbar ranges between the minimum and the maximum. The maximum value is LargeChange+1. The rationale behind this is that, the scrollbar has a property called Value, which represents the position of the top of the thumb. The size of the scrollbar's thumb is equal to the page value or LargeChange. When we reach the last page, or when the thumb reaches the end of the scroll bar, whereupon, we cannot scroll any further, the value property will always be shown less than the maximum. We shall now beguile you with the concepts of the vertical scroll bars. The explanation for the horizontal scroll bars is much the same. The function vmf, which is called from the constructor, initializes 3 variables. The Height of the Vertical scroll bar is 152 pixels because the Size property of the scrollbar is initialized to this value. The Height of the picture is 96 pixels because the Size property of the picture is initialized to this value. The difference between these two heights, which is 56 pixels, is stored in the variable hsb. The variable named ticks stores a value, which is within a range that the vertical scroll bar can handle. A Tick happens to be the smallest increment that a scrollbar can make. In our case, as the Minimum is -100 and the Maximum is 100, the range of value stored in ticks becomes 200. This variable represents the ticks that the scroll bar needs in order to move from one end to the other. In the same vein, the variable hsb denotes the amount of pixels the image needs to be relocated from one end of the scroll bar to another. This is calculated by subtracting the height of the image from the height of the scroll bar.

Screen 4.44 Dividing 56 (the number of pixels to be moved) by 200 (the total number of ticks available), gives us a value of .28. Thus, every tick moved by the scroll bar moves the image by .28 pixels. This value, known as the 'pixels per tick', is stored in vm. A similar routine is followed for the hmf function while implementing a horizontal scroll bar. The labels initially display zero, since this is the default value stored in the property value. The cursor property of the picture is changed to SizeAll. As a result of this, whenever the cursor moves into the picture, it's shape changes into a four-headed monster. At the outset, we want to move the picture downwards. So, we click on the vertical scroll bar. This movement calls the vScroll function. In this function, we initialize the Text property of the label l2 to the Value property of the vertical scroll bar v. Then, variable vp is calculated, whose value is the current value of the scroll bar, i.e. the value of the Minimum property of the scroll bar.

Page 99: C#   classes

Every downward movement of the scroll bar increments the Value property by 1. This is because, the value of property SmallChange has been initialized to 1. Thus, the value of vp will commence at 100 and will keep escalating thereafter. The value stored in the Bottom property of the scroll bar is 176 and the Height of the picture is 96 pixels. Thus, the picture will start at 52 pixels, and then, the distance between the picture and the bottom property will start reducing. The formula for computing the top position where the picture should be placed is as follows: the Bottom of the scroll bar, minus the pixels per tick, multiplied by the current value of the property Value, minus the Height of the picture, plus the value of Minimum. Now, let us do the reverse i.e. let us move the picture around and see how the scroll bars behave. Also, let us observe the corresponding transformation in the values. To move the picture, we first have to click on it with the left mouse button. This calls function pDown. In this function, the value of variable d, which is a boolean, is set to true. Simultaneously, in the function pUp, the value of variable d is set to false. The parameter e of MouseEventArgs has two members X and Y, which furnish the co-ordinates of the mouse pointer with regard to the picture, rather than the window or form. We save these values in the variables ox and oy, which will be utilized to calculate the distance that the picture has been dragged. The function pMove is the focus of all attention since, this is where the real excitement action lies! We place all the code in a large if statement, which results in true when the variable d is true. This occurs only when the mouse button is depressed. Each time the function is called, the minimum and maximum values of the scroll bars are stored in 4 variables. This is futile, since the values always remain constant. We calculate a variable called Value as follows: The initial Y position of the mouse before its dragging commenced, minus the current Y position of the mouse. The result is then divided by the multiplier to convert the pixels into scroll bar ticks. This simulates the scroll bar scrolling in the opposite direction. If the new value of variable Value is less than the minimum value allowed for the scroll bar, the property of the scroll bar v is set to the minimum possible. If the value is larger than the maximum permissible value, then it is initialized to the largest possible value. This concept has been explained in the small note we earlier presented on scrolling. If none of the above hold true, then we merely change the property Value of the scroll bar to the variable Value. These are simple error checks. The labels l1 and l2 are also updated. The above explanation is also relevant for the horizontal scroll bar. The property Top of the picture has to be updated to take into account the new position. This is also stored in the variable value and computed as follows: The original Top property value, plus the current position of the mouse, minus the position of the mouse before the dragging commenced. Like before, we make sure that we do not exceed the Top and Bottom limits of the scroll bar. The picture has to be contained within the scroll bars. If it is smaller than the Top property of the scroll bar, we change the value to that of the Top property. The same holds true for the Bottom. Finally, we initialize the Top property of the picture to Value and do the same for the Left property. Up Down Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form {DomainUpDown u; NumericUpDown n;DomainUpDown a;public zzz() {ClientSize = new Size(504, 352);n = new NumericUpDown();n.Location = new Point(132, 132);n.Maximum = new System.Decimal(100d);n.Minimum = new System.Decimal(0d);n.DecimalPlaces = 2;n.Text = "0.00";Controls.Add(n);a = new DomainUpDown();a.Location = new Point(152, 32);a.Size = new Size(120, 23);a.SelectedItemChanged += new EventHandler(abc);Controls.Add(a);u = new DomainUpDown();u.Location = new Point(152, 64);u.SelectedItemChanged += new EventHandler(pqr);Controls.Add(u);a.Items.Add(new yyy("Center",(int) HorizontalAlignment.Center));

Page 100: C#   classes

a.Items.Add(new yyy("Left",(int)HorizontalAlignment.Left));a.Items.Add(new yyy("Right",(int)HorizontalAlignment.Right));u.Items.Add(new yyy("Left",(int)LeftRightAlignment.Left));u.Items.Add(new yyy("Right",(int)LeftRightAlignment.Right));u.SelectedIndex = 1;}void pqr(object s, EventArgs e){yyy c = (yyy)(u.Items[u.SelectedIndex]) ;n.UpDownAlign = (LeftRightAlignment)(c.i);}void abc(object s, EventArgs e){yyy c = (yyy)(a.Items[a.SelectedIndex]) ;n.TextAlign = (HorizontalAlignment)(c.i);}public static void Main(){Application.Run(new zzz());}class yyy{public string s;public int i;public yyy(string sz, int n){s=sz;i=n;}public override string ToString(){return s;}}}

Screen 4.45 In the above program, we have a NumericUpDown control called n. A NumericUpDown control is used when we have a single numeric value that needs to be incremented or decremented.

Screen 4.46

Page 101: C#   classes

This can be done by clicking on the Up and Down buttons of the control respectively. This control has a property called ReadOnly, which has a value of False by default. This allows entry of a value directly in the control if the user finds it too bothersome to click on the buttons to do so. From this perspective, it operates akin to a text box. The properties Minimum and Maximum specify the minimum and maximum values that the control can accept. Under no circumstances will the control permit us to exceed the range specified by the above two properties. Thus, error checking has been built into the control. A NumericUpDown control has a large number of properties such as DecimalPlaces, Hexadecimal, ThousandsSeparator etc. These properties format the value that is displayed using the Text property. Their roles are as follows:

• The DecimalPlaces property controls the number of decimal places.• The Hexadecimal property displays numbers in hexadecimal format. • The ThousandsSeparator property decides on the character to be used to separate the 1000s. This is because the comma is not the universally accepted separator.

The Increment property of the control which has a default value of 1 decides on the amount of increase/decrease in the number. each time we click on the up or down buttons. n.Increment=10; will increase the number by 10. The two functions ParseEditText and UpdateEditText get called with every change in the number. Thereafter, two DomainUpDown controls, which behave in a manner similar to a NumericUpDown control, are created. These controls display a string instead of a number. Thus, the value passed to this control can be of any class, since all classes are derived from object. The user can also type in text directly. The value typed in must obviously match an item in the collection. The ToString function of the object is called, to display the value in the up-down control. The DomainUpDown control has a property called Items, that returns a DomainUpDown.DomainUpDownItems object. This object represents the object collection. Thus, we can use the Add or Remove methods from the above collection to add or remove the items individually. The Sort property sorts the collection. We pass an object like yyy to the Add function, which accepts a string and an enum called HorizontalAlignment. The enum value is stored in the variable i to facilitate its retrieval at a later stage. Each time we click on the control, function abc gets called. This is because the SelectedItemChanged event has been initialized to this function. In this function, we use the property SelectedIndex, which returns a number depicting the item that has been selected. The property is used as an array index. It returns the yyy object at that index. We store this object in c and then, access the variable i stored in the object yyy. Thus, the entire object yyy is stored as part of the collection. The string and the int can both be accessed together. If the SelectedIndex property is not set, it will not display any value in the control. DateTimePicker Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form {DateTimePicker d;public zzz() {ClientSize = new Size(504, 293);d = new DateTimePicker();d.Location = new Point(24, 24);d.CalendarFont = new Font("Times New Roman", 8f);d.Size = new Size(200, 20);d.CalendarForeColor = System.Drawing.SystemColors.WindowText;d.ShowCheckBox = true;d.ForeColor = System.Drawing.SystemColors.WindowText;d.Format = System.Windows.Forms.DateTimePickerFormat.Custom;d.BackColor = System.Drawing.SystemColors.Window;d.CustomFormat = "\'Date : \'yy MM d - HH\':\'mm\':\'s ddd";d.Anchor = AnchorStyles.Top|AnchorStyles.Right | AnchorStyles.Left;DateTime now = DateTime.Now;d.Value = now;Controls.Add(d);}public static void Main(){Application.Run(new zzz());}}

Page 102: C#   classes

Screen 4.47 Screen 4.48 On running the above program, we see a listbox displaying a date. This date is the system date. You may wonder as to what is the big deal about displaying a simple date. But, when you click on the down arrow of the list box, a fine-looking calendar springs up! The current date is shown highlighted in red. If you click on any date, it will be instantly displayed in the list box. Once the date has been chosen, the calendar vanishes. You can change the month by clicking on the arrows, which are on the left and right of the month. Now, let us get behind the scenes and unravel the mysteries of this program. We first create an object d as an instance of class DateTimePicker, thus encapsulating the standard Windows date time picker control. The control has all the standard properties like Location, Size, Color etc. The default size, in the case of the DateTimePicker, is a width of 200 pixels and a height of 23 pixels. The colors of the control can be changed using CalendarForeColor and ForeColor. The property CalendarFont decides on the font to be used to display the dates.

Screen 4.49 The ShowCheckBox property , which has the default value of false, is set to true, in order to display a check box on the extreme left of the date displayed in the control. The checkbox presently is checked. If the checkbox is checked, the date is valid. If not, the date is said to be unset. The Format property of the control, which is initialized to Custom, decides the display format of the date and time in the control. This property can be initialized to any four of the following enums: Custom, Short, Long and Time. The last three values use the operating system's format options. Since the Format property has been given the value of Custom, the property CustomFormat decides on the display format of the date in the control. This property is initialized as follows: The word Date:, followed by the year, month and day separated by a space, followed by a minus sign and finally, the time. The three ‘ddd’ represent the day of the week in words. The Now property of the DateTime class, that returns the current day, is finally assigned to the Value property of the picker class, which then gets displayed in the control. LinkLabel Control:

a.csusing System;using System.Windows.Forms;using System.Drawing;public class zzz : Form {PropertyGrid pg;LinkLabel l;Panel p;GroupBox g; public zzz() {l = new LinkLabel();l.DisabledLinkColor = (Color)System.Drawing.Color.Blue;l.ForeColor = (Color)System.Drawing.Color.Gainsboro;

Page 103: C#   classes

l.Location = new System.Drawing.Point(32, 128);l.BackColor = (Color)System.Drawing.Color.Transparent;l.LinkArea = new LinkArea(13, 28);l.Font = new System.Drawing.Font("Tahoma", 12f, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.World);l.Text = "please click on sonal to see a message box";l.Size = new System.Drawing.Size(136, 96);l.LinkClicked += new LinkLabelLinkClickedEventHandler(abc);ClientSize = new System.Drawing.Size(504, 445);pg = new PropertyGrid();pg.Dock = System.Windows.Forms.DockStyle.Fill;pg.Location = new System.Drawing.Point(3, 16);pg.CommandsVisibleIfAvailable = true;pg.Text = "propertyGrid1";pg.Size = new System.Drawing.Size(242, 405);pg.SelectedObject = l ;g = new GroupBox();g.Location = new System.Drawing.Point(248, 16);g.Anchor = AnchorStyles.Top|AnchorStyles.Right | AnchorStyles.Left;g.Text = "LinkLabel Properties";g.Size = new System.Drawing.Size(248, 424);g.Controls.Add(pg);Controls.Add(g);p = new Panel();p.Size = new System.Drawing.Size(200, 320);p.Location = new System.Drawing.Point(24, 40);p.BackgroundImage = (Bitmap) new Bitmap("hikingboot.bmp");p.Controls.Add(l);Controls.Add(p);}void abc(object sender, LinkLabelLinkClickedEventArgs e){MessageBox.Show("hi") ;l.LinkVisited = true ;}public static void Main() {Application.Run(new zzz());}}

To avoid any exception from being thrown, you should copy the file hikingboot.bmp to the current directory. Though the above program is not very sizeable, its achievements are substantial. The output of this program shows our screen divided into two parts as follows: (a) The left pane has a picture in the background with some text that behaves like a hyperlink. Clicking on the hyperlink displays a message box with the message 'hi' and changes the color of the hyper link.

Screen 4.50

Page 104: C#   classes

(b) On the right hand side, we come across a large number of properties. When we click on the plus sign in front of the font property, yet more properties get displayed. On varying the Font, the font of the text displayed in the window on the left side changes. You can experiment with the other properties too. Thus, you can achieve a lot without writing tons of code! l is an instance of LinkLabel, which relates to text that can be displayed as a hyper link. The DisabledLinkColor property of LinkLabel decides the color of the hyperlink when it is disabled. The ForeColor and the BackColor specify the foreground and the background color of the control respectively. We may not intend to display the entire string as a hyperlink. So, we use the LinkArea property to ascertain the text that is to be hyper linked. The default position begins at 0,0. The GraphicsUnit enumeration in the Font property specifies a unit of measurement. The value displayed uses 1/75 of an inch, since the unit of measure selected is World. We could also use any of the other 6 values in the enum, such as pixel, millimeter etc.

Screen 4.51 The Text property is the most significant property, as it decides the text to be displayed. The LinkClicked is the event property that will call function abc each time we click on the link. In this function, we display a Message Box and change the LinkVisited property to True, which results in a change in the color of the hyper link. You may observe that this LinkLabel instance l is not added to the form. Next, we create an instance of a PropertyGrid class and store it in pg. The Dock property decides on the edge of the container that this property will be docked to. The usage of the Fill style allows docking of the property on all sides. The property CommandsVisibleIfAvailable displays the command pane only for those objects that expose verbs. As always, the Text property is initialized to some text that does not get displayed. The property of SelectedObject is assigned the LinkLabel control, thereby linking it into the grid and facilitating browsing of the LinkLable control properties. This property also allows us to browse multiple objects. Finally, we add this object to the Form, thereby indirectly adding the hyperlink also. A group box is merely an anthology of other controls. The Group box control is the one that contains the PropertyGrid object. We also create an instance of a Panel object, which, like a GroupBox class, contains other controls. If the Enabled property in the panels is set to False, all the controls within it will be disabled. The panel control, by default, is drawn without any Borders. The BorderStyle property provides us with two-dimensional or three-dimensional borders, to distinguish a panel from other areas of the form. The Panel class can also contain scrollbars. The Panel class has a property called BackgroundImage that selects the picture to be displayed in the panel. This Panel class has the LinkLabel added to it, to which we add the Panel. Thus, we have two controls that have been added directly to the form, the Panel that has the LinkLabel and the GroupBox that has the PropertyGrid.

Page 105: C#   classes

ListBox Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form{sss p;ListBox l;Button b;ColorDialog c;ImageList i;public zzz(){b = new Button();b.Location = new System.Drawing.Point(16, 200);b.Text = "Color";b.Size = new System.Drawing.Size(75, 23);b.Click += new EventHandler(abc);Controls.Add(b);c = new ColorDialog();p = new sss();p.Location = new System.Drawing.Point(64, 90);p.Size = new System.Drawing.Size(64, 168);Controls.Add(p);i = new ImageList();i.ImageSize = new Size(24, 22);i.Images.Add(new Bitmap("club.bmp"));i.Images.Add(new Bitmap("diamond.bmp"));i.Images.Add(new Bitmap("heart.bmp"));i.Images.Add(new Bitmap("spade.bmp"));Size = new System.Drawing.Size(512, 320);l = new ListBox();l.ForeColor = (Color)System.Drawing.SystemColors.WindowText;l.Location = new System.Drawing.Point(8, 24);l.IntegralHeight = false;l.Size = new System.Drawing.Size(232, 60);l.ColumnWidth = 144;l.SelectedIndexChanged += new EventHandler(pqr);l.Items.AddRange (new object[] {"a1", "a2", "a3", "a4"});l.SelectionMode = SelectionMode.MultiSimple;Controls.Add(l);}void abc(object sender, EventArgs e){if (c.ShowDialog() == DialogResult.OK){l.ForeColor = c.Color;}}void pqr(object sender, EventArgs e){p.ci();int[] se = new int[l.SelectedIndices.Count];l.SelectedIndices.CopyTo(se, 0);for (int i=0; i<se.Length; i++){int ind = se[i];object it = l.Items[ind];string s = it.ToString();Image im = aaa(s);p.ai(im);}p.Invalidate();}Image aaa(string b){if (b.Equals("a1")){return i.Images[0];}

Page 106: C#   classes

else if (b.Equals("a2")){return i.Images[1];}else if (b.Equals("a3")){return i.Images[2];}else if (b.Equals("a4")){return i.Images[3];}else{return null;}}public static void Main(){Application.Run(new zzz());}}public class sss : Panel{Image[] i = new Image[4];int Cnt=0;public virtual void ai(Image img){i[Cnt++] = img;}protected override void OnPaint(PaintEventArgs pe){base.OnPaint(pe);for (int j=0; j< Cnt; j++){pe.Graphics.DrawImage(i[j], new System.Drawing.Point(0, 30 * j + 5));}}public virtual void ci(){Cnt = 0;}}

Before running the executable file, the 4 files with the 'bmp' extension must be copied from one of the sample directories into the current directory.

Screen 4.52 Screen 4.53 When we run the above program, we see a list box with 4 values and a button labeled 'Color'. When we click on the button, it displays a color dialog box, which permits us to choose from amongst a plethora of colors. We can select a color and then, click on the OK button. As a result, the color of the list box items changes to the selected color. We are also allowed to create our own custom colors in the dialog box.

Page 107: C#   classes

Screen 4.54 Screen 4.55 Each time we select an item from the list box, an image representing the item is displayed. If we reselect an item that we had selected earlier, the currently selected image gets replaced by the latest selected one. In the program, a Button b is created. It calls function abc whenever it is clicked. Then, an instance c of class ColorDialog is created. We have created the class sss. It is derived from the class Panel. It has a member i, which is an array of 4 images and an int variable named Cnt. The variable Cnt is used as an index to access the images in the array, and to store the count of the number of images. When we add the sss object to our Form, nothing gets displayed in the window. This is because, there are no controls in the panel. Next, we create an ImageList object called i. This class stores a collection of images that can be used by other controls such as the Toolbar or ListView. Bitmaps or Icons are added to this class so that they are available for exploitation by other controls. Images is a property of type ImageList.ImageCollection, whose Add method is used to add a bitmap to the collection. We add a total of 4 bitmaps to our ImageList object. The ListBox object l has a property ColumnWidth, that decides on the width, in pixels, of each column in the ListBox. In a multi-column ListBox, the ColumnWidth property refers to the width of each column. A value of zero is connotative of the fact that each column has a default width, taking into account the data displayed in them. The property IntegralHeight decides whether partial items can be displayed or not. If the value is set to true, then only complete items are displayed. In this case, the ListBox will be resized to ensure that partial items are not displayed. Each time we select an item, the Event SelectedIndexChanged gets kick started. This event, in turn, calls function pqr. Every list box has a property called Items, which is a collection of items displayed in the list box. The data type of Items is ListBox.ObjectCollection. It has a method called AddRange, which accepts an array of Child Controls present in the list, sorted on the index. Since we want the list box to display the text a1, a2, a3 and a4, we initialize the object array to this array of strings. The property SelectionMode decides on the number of items that can be chosen or selected concomitantly. Four options available:

• None : This means that no items can be selected. In effect, it disables the ListBox. • One : This allows us to select only one item at a time, • MultiSimple : This lets us choose multiple items at the same time • MultiExtended : This enables us to choose multiple items at a time and allows us to use keys like SHIFT and CONTROL combinations to select the multiple options.

We finally add the list box to the Form. Clicking on the button labeled Color calls ShowDialog from the ColorDialog class, which displays a dialog box with a zillion colors. Until we click on the OK button or the Cancel button, we cannot leave the dialog box. The button returns one of two values:

• DialogResult.OK if we click on OK button.• DialogResult.CANCEL if we click on the Cancel button.

If the user opts to click on the OK button, the color selected is assigned to the ForeColor property of the ListBox.The function pqr is the vortex of action. The function is called when any item is selected or unselected from the list box. We first call function ci from the class sss, which initializes the variable Cnt to zero. The SelectedIndices property returns a collection object named ListBox.SelectedIndicesCollection, which lists all the items that are currently selected in the list box. This is so because we can select multiple items from the list box. First, an array called se of type int is created, depending on the number of items selected. Then, the selected indices are copied to the array. The variable ind in the for statement refers to each index selected. The Items array returns an object that stores the value of each index. As we need the string representation of the selected item, function ToString is used to convert the object into a string. This facilitates the possibility of having a list box containing pictures. We then call the function aaa that accepts a string representing the ListBox item selected, which can be either a1, a2, a3 or a4. It returns an image representing the string. It then checks the value of the string passed and returns an image stored in

Page 108: C#   classes

the ImageList object. The function ai merely uses the variable Cnt to index the array of images with a new picture. Thus, if we select three images, the array i will contain three pictures. When the for loop terminates, we call the function Invalidate. As we had learnt earlier, function OnPaint displays all the pictures. This is achieved using the Cnt variable that contains the number of images stored in the Array. Thus, we use the ImageList class to store the images and the array of images in the sss class to store the images that need to be displayed each time. StatusBar control

a.csusing System;using System.Drawing;using System.Windows.Forms; public class zzz : Form {System.ComponentModel.Container c;StatusBar s;StatusBarPanel s1;StatusBarPanel s2;StatusBarPanel s3;Timer t;public zzz() {c = new System.ComponentModel.Container();s2 = new StatusBarPanel();t = new Timer(c);s = new StatusBar();s3 = new StatusBarPanel();s1 = new StatusBarPanel();s2.AutoSize = (StatusBarPanelAutoSize)2;s2.Alignment = HorizontalAlignment.Right;s2.Width = 76;s3.AutoSize = StatusBarPanelAutoSize.Contents;s3.Width = 20;s1.BorderStyle = StatusBarPanelBorderStyle.None;s1.Icon = new Icon("status.ico");s1.AutoSize = StatusBarPanelAutoSize.Contents;s1.Width = 62;s1.Text = "sonal";Size = new System.Drawing.Size(512, 320);KeyUp += new KeyEventHandler(abc);t.Interval = 1000;t.Enabled = true;t.Tick += new EventHandler(pqr);s.Size = new Size(212, 20);s.Location = new Point(0, 216);s.BackColor = (Color)SystemColors.Control;s.Text = "Vijay Mukhi";s.ShowPanels = true;s.Panels.AddRange((StatusBarPanel[])new StatusBarPanel[] {s1, s2, s3});Controls.Add(s);s3.Text = "OVR";}void abc(object se, KeyEventArgs e){if (e.KeyCode == Keys.Insert){string s = this.s3.Text;if (s.Equals("INS"))s3.Text = "OVR";elses3.Text = "INS";}}void pqr(object se, EventArgs e){DateTime t = DateTime.Now;string s = t.ToLongTimeString() ;s2.Text = s ;}public static void Main(){Application.Run(new zzz());}}

Page 109: C#   classes

Every Windows application displays a StatusBar control. This program is going to introduce the status bar in our window. In order to accomplish this, the file status.ico needs to be copied from one of the sample directories into the current directory. The above program merely presents a blank window with a status bar at the bottom. The status bar has the following contents:

• an icon.• the word 'sonal'.• the time ticking away in the middle.• the status of the Insert key.

Screen 4.56 Screen 4.57 Each time we use the insert key, the text toggles between INS and OVR. The class Container encapsulates zero to one or more components. We create three status bar objects s1, s2 and s3, which are instances of class StatusBarPanel. This class, in turn, is derived from class Component. It stores the StatusBar control panel information. The object s is an instance of class StatusBar, which represents a Window status bar control. This control has no panels by default. The property AutoSize in the StatusBarPanel class regulates the changes occurring in the panel of a status bar, whenever the status bar is resized. The values are obtained from the enumeration StatusBarPanelAutoSize that has the following three values:

• Contents: As is evident from the name, the contents of the status bar decide how its size will change. • None: The status bar panel does not change whenever the status bar is resized.• Spring: The panel shares the available space with all other panels having a setting of Spring, after yielding space to the panels having either the Contents or None settings.

Text can be aligned in a status bar panel in 3 ways with reference to the status bar, i.e. Left, Right and Center. The Alignment property, by default, has a value of Left. For the status bar panel s2, we have set it to Right. The default width is 100 pixels for a status bar panel. An Icon can be specified along with the text that is displayed. To do so, we initialize the Icon property to a .ico file. An .ico file is a small transparent bitmap image. The KeyUp Event is fired whenever a key is released in the focused control. In our case, function pqr is called. The Timer class, that implements a Windows timer, merely activates an event at a particular time interval. This timer, which can be used in a window, is designed for a single threaded environment only, where User Interface threads are being executed. The Interval property decides the time, in milliseconds, between timer ticks. The Enabled property sets the timer On. The garbage collector does not interfere with the timer while it is running. The Event Tick will call the function pqr whenever the time interval set in the timer elapses. In our case, it is occurs after one second. The StatusBar is made up of panels. The ShowPanels property displays all the panels that are added using the AddRange method, i.e. s1, s2 and s3. Finally, the text of the last panel is set to OVR and the StatusBar object is added to the form. The function abc gets called each time we press a key in our form. This function is called with the parameter e of KeyEventArgs, which has a member KeyCode, which contains a number corresponding to the key pressed. As it is difficult to remember the numbers assigned to every key, the enumeration Keys is used to represent the keys. If the Insert key is pressed, the current Text displayed in the status bar panel s3 is retrieved and the value gets toggled from OVR to INS. On completion of the timer interval, we use the Now property of the DateTime class to provide us with the current time. This is then supplied to s2.Text, which updates the status bar panel with the current time every second. What you can display in a status bar is limited by your imagination. It is common to display the status of keys and the time on the status bar. Tab Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form {

Page 110: C#   classes

GroupBox g2;GroupBox g1;ImageList i; TabPage t1; TabPage t2;TabControl t; public zzz() {ClientSize = new Size(546, 293);g1 = new GroupBox();g1.Location = new Point(12, 16);g1.Text = "Sonal";g1.Size = new Size(202, 144);g2 = new GroupBox();g2.Location = new Point(12, 16);g2.Text = "Vijay mukhi";g2.Size = new Size(202, 128);i = new ImageList();t1 = new TabPage();t2 = new TabPage();t = new TabControl();t1.Text = "Mukhi";t1.Size = new Size(224, 193);t1.ImageIndex = 0;t1.TabIndex = 0;t2.Text = "Vijay";t2.ImageIndex = 1;t2.TabIndex = 1;t.Location = new Point(24, 32);t.Size = new Size(232, 220);t.SelectedIndex = 0;t.ImageList = i;i.Images.Add((Bitmap)new Bitmap("calendar.bmp"));i.Images.Add((Bitmap)new Bitmap("note.bmp"));t.ImageList = i;Controls.Add(t);t1.Controls.Add(g1);t2.Controls.Add(g2);t.Controls.Add(t1);t.Controls.Add(t2);}public static void Main() {Application.Run(new zzz());}}

This program requires two bitmaps called note.bmp and calendar.bmp. We start by creating two GroupBox controls named g1 and g2, and change the properties of Locations, Size and Text. The ImageList i stores a list of images. We next create two TabPage objects t1 and t2. A TabPage class implements a single page of the TabControl class. It is a panel class having the properties of a TabItem.

Screen 4.58 Screen 4.59 The string assigned to the Text property is displayed as the tab page text. We can set it to a certain size. The ImageIndex property is an index into the ImageList list object, which is associated with the TabControl. The TabPage objects are added to the TabControl using the Controls collection. The ImageList property is initialized to the list of images denoted by the

Page 111: C#   classes

ImageList class. Thus, the ImageIndex member decides on the image that will be displayed along with the Text in the TabControl. A TabControl shows a list of TabPages. Clicking on the tab activates them. A TabControl, in other words, is like a series of Dialog boxes containing controls, organized in a logical fashion. The controls that would earlier have appeared in one large dialog box, are now placed in separate dialog boxes. As mentioned earlier, we add the GroupBox to the Control collection of the individual TabPage objects t1 and t2. The controls that we require are added to the GroupBox. The GroupBox, in turn, gets added to the TabPage. From then on, the TabControl takes over and we can flick between TabPages with ease. ToolTip Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form {System.ComponentModel.Container c;ToolTip t;PictureBox p1;PictureBox p2;public zzz(){c = new System.ComponentModel.Container();t = new ToolTip(c);t.Active = true;t.ShowAlways = true;t.AutomaticDelay = 100;t.AutoPopDelay = 100;t.InitialDelay = 100;t.ReshowDelay = 100;p1 = new PictureBox();p2 = new PictureBox();Size = new Size(512, 300);p1.Location = new Point(8, 7);p1.Size = new Size(20, 20);p1.Image = new Bitmap("open.bmp");t.SetToolTip(p1, "vijay");Controls.Add(p1);p2.Location = new Point(28, 7);p2.Size = new Size(20, 20);p2.Image = new Bitmap("new.bmp");t.SetToolTip(p2, "mukhi");Controls.Add(p2);}public static void Main(){Application.Run(new zzz());}}

All of us need help at some point in time. The ToolTip control is just the antidote for all maladies. Before running the executable, copy the two bitmaps open.bmp and new.bmp to the current directory.These two bitmaps will be displayed as two separate images on our form. Whenever we move our cursor close to them, a yellow colored help text comes into sight. A ToolTip class gives us a small window that contains a single line of text that describes what a control stands for. The real productivity of Bitmaps and Icons can be fully harnessed only if we are able to discern their functionality. The Active property, if set to false, disables the ToolTip control. The ShowAlways property will show a ToolTip despite the fact that the control it is assigned to, is currently disabled.

Page 112: C#   classes

Screen 4.60 Screen 4.61 The following four properties have been set for the Tooltip control:

• AutomaticDelay: It denotes the time in milliseconds that elapses before the tool tip appears. The default is 500 milliseconds. • AutoPopDelay: It is the time in milliseconds for which the ToolTip remains visible to the user while the mouse is stationary on the control. Its default value is 10 times the value of the property AutomaticDelay. • InitialDelay: It is the time in milliseconds for which the mouse must remain fixed within the control, before the tool tip becomes visible again. The default value is equal to that of property AutomaticDelay. • ReshowDelay: It is the time in milliseconds that elapses, as the mouse moves from one control to another. The default is 1/5 of the property AutomaticDelay.

Any amendment made to the property AutomaticDelay has an impact on the above properties. Thus, it is advisable to first change the AutomaticDelay property, followed by the other properties. Two PictureBox controls have been added in our window. We add these two pictures to the form. This class can display all types of images, including bitmaps, icons, JPEG, GIF and many more. The Image property decides on the image that gets displayed. By default, the image has no borders and can be clipped too. The ToolTip class has a method called SetToolTip, which accepts a control as the first parameter and the text of the tool tip as the second. Henceforth, whenever the mouse is positioned on that control, the tool tip will be displayed. TrackBar Control

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz : Form{TrackBar t;Label l;public zzz() {Size = new Size(512, 320);t = new TrackBar();t.Location = new Point(8, 24);t.Size = new Size(200, 42);t.ValueChanged += new EventHandler(abc);t.TickFrequency = 5;t.Minimum = 0;t.Maximum = 100;t.SmallChange = 5;t.LargeChange = 25;t.Orientation = Orientation.Horizontal;t.TickStyle = TickStyle.Both;l = new Label();l.Location = new Point(112, 192);l.Text = t.Value.ToString();Controls.Add(t);Controls.Add(l);}void abc(object source, EventArgs e){l.Text = t.Value.ToString();}public static void Main() {Application.Run(new zzz());}}

Page 113: C#   classes

The TrackBar class resembles a scroll bar in many respects, but its interaction with the user is distinct. We can configure values that the TrackBar represents and also define increments for off-button clicks. The TrackBar can be positioned horizontally or vertically. The number of ticks that are displayed can also be configured. Each time that the value of the TrackBar changes, the Event property ValueChanged calls the function abc. The property TickFrequency specifies the number of ticks that will be drawn. The lower limit of the range of the TrackBar is confined to a value decided by the Minimum property, which in our case is 0. The upper limit of the range of the TrackBar is confined to a value determined by the Maximum property, which in our case is 100.

Screen 4.62 Screen 4.63 We may not want to draw 100 ticks to represent all the values. So, we specify a value such as 5 as the TickFrequency. This will draw a tick mark at a spacing of 5, resulting in a total of 20 ticks. Thus, each tick will represent 5 units of the TrackBar's range of values. The property SmallChange decides on the magnitude of change that occurs in the TrackBar, whenever the user presses the Up arrow key or the Down arrow key to move the TrackBar thumb. The value assigned to the LargeChange property is used whenever we click on the side, or use the Page Up and Page Down keys. The Orientation property can take only two values i.e. horizontal or vertical. The TickStyle property uses an enumeration that can have four values:

• None: for no tick marks. • Both: for tick marks on both sides of the control. • BottomRight: for tick marks at the bottom for a horizontal control, and on the right for a vertical control .• TopLeft: this is the reverse of BottomRight.

We then use a label to display the value of the track bar on the form. This value is stored in the property Value of the label class. Finally, we add the label and TrackBar. Function abc merely updates the label to depict the current position of the TrackBar. Tree-Node Control

a.csusing System;using System.IO;using System.Resources; using System.ComponentModel;using System.Drawing;using System.Windows.Forms;using System.Runtime.InteropServices;public class zzz : Form{TreeView d;ImageList i;public zzz(){ClientSize = new System.Drawing.Size(502, 293);i = new ImageList();i.Images.Add(new Bitmap("clsdfold.bmp"));i.Images.Add(new Bitmap("openfold.bmp"));d = new TreeView();d.ImageList = (ImageList)i;d.ForeColor = (Color)System.Drawing.SystemColors.WindowText;d.Location = new System.Drawing.Point(24, 16);d.AllowDrop = true;d.Indent = 19;

Page 114: C#   classes

d.Text = "treeView1";d.SelectedImageIndex = 1;d.Size = new System.Drawing.Size(200, 264);d.AfterSelect += new TreeViewEventHandler(sss);d.BeforeExpand += new TreeViewCancelEventHandler(eee);Controls.Add(d);string[] dr = Environment.GetLogicalDrives();for (int ii = 0; ii < dr.Length; ii++){if (PlatformInvokeKernel32.GetDriveType(dr[ii]) == PlatformInvokeKernel32.DRIVE_FIXED) {TreeNode c = new TreeNode(dr[ii]);d.Nodes.Add(c);ddd(c);}}}void ddd(TreeNode n){DirectoryInfo dir = new DirectoryInfo(ppp(n));DirectoryInfo[] e = dir.GetDirectories ();for (int i = 0; i < e.Length; i++){string na = e[i].Name; if (!na.Equals(".") && !na.Equals("..")){n.Nodes.Add(new TreeNode(na));}}} void sss(object source, TreeViewEventArgs e){Text = "Windows.Forms File Explorer - " + e.Node.Text;} void eee(object source, TreeViewCancelEventArgs e){TreeNode n = (TreeNode)e.Node;for (int i = 0; i < n.Nodes.Count; i++){ddd(n.Nodes[i]);}}string ppp(TreeNode node){if (node.Parent == null){return node.Text;}return Path.Combine(ppp(node.Parent), node.Text);}[STAThread]public static void Main(string[] args) {Application.Run(new zzz());}} public class PlatformInvokeKernel32 { [DllImport("KERNEL32", CharSet=System.Runtime.InteropServices.CharSet.Auto)] public static extern int GetDriveType(string lpRootPathName); public const int DRIVE_FIXED = 3; }

Copy the two bmp files clsdfold.bmp and openfold.bmp required for this program, from the samples directory into your current directory. Here, we first create a TreeView control called d. A TreeView displays a list of items or nodes in a hierarchical manner. A node consists of a caption and a bitmap; the bitmap is optional. The user can select a node and collapse it or expand it by clicking on the plus or minus sign, displayed alongside, respectively. We have an ImageList control i that represents two images. We initialize the ImageList property of the TreeView control to the Image List. Thus, the TreeView can now use the images stored in the ImageList. The AllowDrop property facilitates the use of the control for events and Drag and Drop operations.

Page 115: C#   classes

The property Indent decides the indent in the pixels. The SelectedImageIndex determines as to which picture is to be used from the ImageList when the user selects an item in the control. There are a large number of events that get triggered off, but we are capturing only the following two:

• AfterSelect, that calls function sss• BeforeExapnd, that calls functions eee.

The EventHandler functions use different delegate types. The class Environment has a static function GetLogicalDrives that returns an array of strings, which denotes the logical drives on our machine. If machine has three drives, C:\, D:\and E:\, the for loop is repeated thrice. Using the Platform invoke API, we can call any code in any dll. So, we first call the external function GetDriveType and compare it with the value retrieved for DRIVE_FIXED, which is 3 in this case.If there are 3 fixed drives then the if statement is true for 3 of them, resulting in the display of 3 nodes only. If you have only one fixed drive i.e. C, you will see only one node on your screen.

Screen 4.64 Screen 4.65 A TreeNode object is created, which is used in the TreeView. The constructor is passed the drive name to enable it to be displayed as a label for the node. A TreeView is made up of Nodes. It has a property called Nodes, which is of type TreeNodeCollection. Using the Add member of the collection, we add the tree node. Thereafter, function ddd is called with this newly created and empty tree node object. Function ppp is called with an empty tree node. If the node has no parent, the text associated with the node is returned. But if a parent exists, we use the static function Combine, of the Path class, to combine the two strings together. The Combine function calls the ppp function recursively. To begin with, the value of ppp(n) is C:\. The object dir represents directories in the C drive. To obtain all the directories present on the drive, we call function GetDirectories, which returns an array of DirectoryInfo objects. The Name member returns the name of the directory. If the return value is not a . or .., we add the directory name to the list of Nodes. In the AfterSelect event, function sss gets called. In this function, we change the Text of the form to the node selected. This is possible because the node label is passed as a parameter to TreeViewEventArgs. The function eee recovers the current active node from the parameter passed to it. Using the Count property, we calculate the number of nodes contained in the active node and thereafter, iterate through each of these nodes. The same function ddd is called with every node. This is how we can display the sub-directories contained within directories and the files displayed within the directories. Data is sent to the control, which merely returns the same data in a tree like form. When we click on the first plus (+) sign of C:\, the function ppp(n) returns the entire path name of each directory in a recursive fashion. Docking

a.csusing System;using System.Drawing;using System.Windows.Forms;public class zzz :Form {Panel p1;Panel p2;GroupBox g;Button b;RadioButton r1;

Page 116: C#   classes

RadioButton r2; RadioButton r3; RadioButton r4; RadioButton r5; RadioButton r6; RadioButton rs;Splitter s;public zzz () {g = new GroupBox();rs = new RadioButton();r5 = new RadioButton();s = new Splitter();b = new Button();r6 = new RadioButton();r4 = new RadioButton();r1 = new RadioButton();r3 = new RadioButton();p1 = new Panel();p2 = new Panel();r2 = new RadioButton();Location = new Point(100, 100);SizeGripStyle = SizeGripStyle.Show;ClientSize = new Size(448, 400);g.Location = new Point(16, 152);g.TabStop = false;g.Text = "&Dock";g.Size = new Size(88, 176);s.BackColor = Color.Blue;s.Dock = DockStyle.Right;s.Location = new Point(325, 0);s.Size = new Size(3, 400);b.BackColor = SystemColors.Control;b.FlatStyle = FlatStyle.Popup;b.Anchor = AnchorStyles.None;b.Text = "Demo Button";rs.Text = "rs";rs.Size = new Size(100, 23);r5.Location = new Point(8, 120);r5.Text = "&Right";r5.Size = new Size(72, 24);r5.Click += new System.EventHandler(abc);r6.Location = new Point(8, 144);r6.Text = "&Fill";r6.Size = new Size(72, 24);r6.Click += new System.EventHandler(abc);r4.Location = new Point(8, 96);r4.Text = "&Bottom";r4.Size = new Size(72, 24);r4.Click += new System.EventHandler(abc);r1.Checked = true;r1.Location = new Point(8, 24);r1.Text = "&None";r1.Size = new Size(72, 24);r1.Click += new System.EventHandler(abc);r3.Location = new Point(8, 72);r3.Text = "&Left";r3.Size = new Size(72, 24);r3.Click += new System.EventHandler(abc);p1.Text = "ButtonPanel";p1.Size = new Size(325, 400);p1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;p1.Dock = DockStyle.Fill;p1.BackColor = Color.Green;p2.Location = new Point(328, 0);p2.Text = "ControlsPanel";p2.Size = new Size(120, 400);p2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;p2.Dock = DockStyle.Right;r2.Location = new Point(8, 48);r2.Text = "&Top";r2.Click += new System.EventHandler(abc);Controls.Add(p2);Controls.Add(s);

Page 117: C#   classes

Controls.Add(p1);p1.Controls.Add(b);p2.Controls.Add(g);g.Controls.Add(r4);g.Controls.Add(r3);g.Controls.Add(r1);g.Controls.Add(r5);g.Controls.Add(r6);g.Controls.Add(r2);rs = r1;aaa(); }void aaa() {if (rs == r1) b.Dock = DockStyle.None;else if (rs == r2)b.Dock = DockStyle.Top;else if (rs == r3)b.Dock = DockStyle.Left;else if (rs == r4)b.Dock = DockStyle.Bottom;else if (rs == r5) b.Dock = DockStyle.Right;else b.Dock = DockStyle.Fill;}protected void abc(object s, EventArgs e){rs = (RadioButton)s;aaa();}public static void Main() {Application.Run(new zzz());}}

This program may be generously proportioned, however it is very simple to comprehend. We have created seven radio buttons, one GroupBox, two panels, one splitter and one button. The SizeGripStyle property can have three values:

• Show: that always shows the sizing grip.• Hide : that always hides the sizing grip.• Auto : that shows and hides the sizing grip as necessary.

The sizing handle is always shown at the right hand corner of the form. The button has a FlatStyle of Popup that determines the appearance of the button. Clicking on any of the Radio Buttons results in a call to the function abc. This is as a consequence of the Event property Click having been set to abc. Next, we add all the controls to the Form. The repetitive code for adding the controls results in a lengthy program.

Screen 4.66 Screen 4.67 The function abc gets called. Its first parameter is an object type. The object stands for the control that calls the function. In function abc, the RadioButton that was clicked on, is represented by s. The value contained in s is stored in another

Page 118: C#   classes

RadioButton called rs. The function aaa is called thereafter. It compares the value contained in rs with the 5 radio buttons r1 to r5. Depending upon the value that matches, the Dock property of the button changes.

Screen 4.68 Screen 4.69 When a control is docked to the edge of a container, it will always take up a position flush against the edge, if the control is resized. The Left, Right, Top and Bottom styles position the control towards the respective edge of the container. Fill will fill upto all the sides of the control. The control is resized to fill the container's edges. The First dock style named None, ensures that the control does not get docked at all.

5 Data Handling

a.csusing 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);}

Page 119: C#   classes

}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);}

Page 120: C#   classes

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);

Page 121: C#   classes

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

Page 122: C#   classes

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

Page 123: C#   classes

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

Page 124: C#   classes

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.

Page 125: C#   classes

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.csusing 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();

Page 126: C#   classes

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

Page 127: C#   classes

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.csusing 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;

Page 128: C#   classes

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;

Page 129: C#   classes

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

Page 130: C#   classes

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.

Page 131: C#   classes

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.csusing 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) {

Page 132: C#   classes

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 therepublic 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;

Page 133: C#   classes

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());}}

Page 134: C#   classes

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

Page 135: C#   classes

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.csusing 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";

Page 136: C#   classes

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.

Page 137: C#   classes

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.csusing 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";

Page 138: C#   classes

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.csusing 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;

Page 139: C#   classes

}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.

Page 140: C#   classes

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.csusing 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.

Page 141: C#   classes

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.csusing 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 5840 5840 0 {Width=584, Height=336}Microsoft Corporation

Page 142: C#   classes

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

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 that of the Bounds property. The co-ordinates are relative to the upper left corner of the client area or window. Thus, they start at X=0 and Y=0. The width and height are used as the drawing surface within which data of the grid is to be placed. The ClientSize property returns a Size object that contains the Height and Width of the DataGrid. These dimensions are akin to those returned by the Bounds property. The Height and Width properties also return the same values. The Left property denotes the leftmost edge and returns zero. The Right property denotes the rightmost edge of the data grid, which happens to be 584. It therefore returns this value. The Location property returns a point whose X and Y co-ordinates have the value 0,0 since that is where the left edge of the DataGrid commences. Finally, the Size property once again returns the same data containing the width and height. Thus, we have a large number of properties, which return the same information. The property ColumnHeadersVisible is similar to CaptionVisible. It is boolean and decides whether the column/field names are to be displayed above the data or not. This row is also called the parent row. The CompanyName property divulges the name of the company that created the control. The answer, quite obviously, is Microsoft Corporation. Every control has a property called Controls that returns a ControlCollection object. The collection constitutes of a list of controls, which form the DataGrid. The Count property in the Collection reports that the DataGrid control is made up of 2 controls. Using the indexer, we access the two individual controls, c1 and c2. The WriteLine function displays these two controls as the vertical and horizontal scrollbars. For the ones who tuned in late, every class has a ToString function that discloses relevant information about the class. The output of the function ToString varies, depending upon what the class wants to reveal about itself. In order to verify that the DataGrid object is fabricated from other controls, we display the value contained in the property HasChildren. The return value is True. DataGrid Events

a.csusing 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() {

Page 143: C#   classes

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.MouseDown += new MouseEventHandler(abc);Controls.Add(d);}void abc(object s, MouseEventArgs e){System.Console.WriteLine(e.X + " " + e.Y + " " + e.Clicks + " " + e.Button) ;}public static void Main() {Application.Run(new zzz());}}

Output87 66 1 Left90 82 1 Right84 101 1 Middle

Screen 5.21 Event handling is the arena, wherein the true ability of a Control comes into focus. We desire that our own event-handling function should be called, every time an event occurs in the DataGrid control. So far, whenever the control has been displayed, none of our code has come into play. In the above example, we desire that whenever the user clicks in the DataGrid, our code should be executed. To accomplish this, we trap one of the Events using the MouseEventHandler delegate, and ensure that the function abc is called every time someone clicks in the DataGrid. The handler functions is always given the control that generated the event as the first parameter. In our case, it is the DataGrid. You can verify this by executing the WriteLine function, which displays the name of the control as System.Windows.Forms.DataGrid. The second parameter to abc is an object that contains the X and Y co-ordinates i.e the location at which the mouse was clicked; the number of times the mouse was clicked; and finally, the button that was employed. You are at liberty to insert the code that you wish to execute, when the event is called. The basic concept is that, certain events that occur in the DataGrid can be trapped and, accordingly, specific user-defined functions can be called. We will focus on some of these events in the forthcoming programs.

a.csusing System;using System.Data;using System.Drawing;using System.Windows.Forms;using System.Data.SqlClient;public class zzz : Form {

Page 144: C#   classes

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.CurrentCellChanged += new EventHandler(abc);Controls.Add(d);}void abc(object s, EventArgs e) {DataGridCell g = d.CurrentCell;int c = g.ColumnNumber ;int r = g.RowNumber; System.Console.WriteLine( "Column " + c + " Row " + r + " " + d[r,c]);d[r,c + 1] = "hi " + r;}public static void Main() {Application.Run(new zzz());}}

OutputColumn 0 Row 1 ANATRColumn 1 Row 2 Antonio Moreno Taquer¡aColumn 2 Row 3 Thomas Hardy

It is more constructive to trap the event CurrentCellChanged, which gets activated whenever a cell is selected from the DataGrid. The function abc gets called with similar parameters as identified by the event in the earlier program. In function abc, using the DataGrid property of CurrentCell of type DataGridCell, we retrieve information such as the Column number, RowNumber etc. of the current cell. The row number is stored in the variable r, whereas the column number is stored in the variable c. The indexer in the DataGrid aids us in accessing the contents of the cell that has been clicked on. Since the indexer requires the row number followed the column number, we supply it with variables r and c. This indexer is read-write. Therefore, it allows us to change the value of any cell in the grid. Here, we have altered the value of the column to the right of the current cell to contain the string 'hi', followed by the row number. This demonstrates the flexibility provided by a DataGrid control.

Screen 5.22 Every minuscule aspect of the grid can be altered at run time, but all properties are not available at design time. The events that a grid can respond to are ever so many to be listed here. The only event that it does not presently respond to is the 'End of the World' event, which as per our conviction, Microsoft is likely to redress in the next version.

Page 145: C#   classes

Master-Detail or Parent-Child Relationship

a.csusing System;using System.Data;using System.Drawing;using System.Windows.Forms;using System.Data.SqlClient;public class zzz : Form {ccc c;DataGrid d;public zzz() {d = new DataGrid();c = new ccc();d.BeginInit();d.PreferredRowHeight = 16;d.Size = new System.Drawing.Size(584, 336);d.DataSource = c;d.DataMember = "Customers";d.ForeColor = System.Drawing.Color.Navy;d.Location = new System.Drawing.Point(8, 8);d.BackColor = System.Drawing.Color.Gainsboro;d.AlternatingBackColor = System.Drawing.Color.WhiteSmoke;ClientSize = new Size(600, 413);c.DataSetName = "CustomersDataSet";Controls.Add(d);d.EndInit();SqlConnection con = new SqlConnection("server=(local)\\NetSDK;uid=QSUser;pwd=QSPassword;database=northwind");SqlDataAdapter Cust = new SqlDataAdapter ("Select * from Customers", con);SqlDataAdapter Ord = new SqlDataAdapter ("Select * from Orders", con);Cust.Fill(c, "Customers");Ord.Fill(c, "Orders");}public static void Main() {Application.Run(new zzz());}}public class ccc : DataSet {cus tc;Orders to;DataRelation r;public ccc() {tc= new cus("Customers");Tables.Add(this.tc);to= new Orders("Orders");Tables.Add(this.to);r = new DataRelation("custord",tc.cID,to.oID);//r = new DataRelation("custord",new DataColumn[]{tc.cID},new DataColumn[]{to.oID});Relations.Add(r);}}public class cus : DataTable{public DataColumn cID;public cus(string name) : base(name) {cID = new DataColumn("CustomerID");Columns.Add(cID);PrimaryKey = new System.Data.DataColumn[] {cID};}}public class Orders : DataTable{public DataColumn oID;public Orders(string name) : base(name) {oID = new DataColumn("CustomerID");Columns.Add(oID);}}

Page 146: C#   classes

First, let us steal a look at what occurs when the above program is run.

Screen 5.23 When the form loads on, we see a list of customers from the Customers table, with a plus sign displayed on the left.

Screen 5.24 Clicking on the plus sign will exhibit a hyperlink with the word 'custord'. When we click on the hyperlink, a list of orders placed by this customer along with the customer details is displayed on the first row. The topmost right hand corner has a back button, which takes us back to the Customers table.

Screen 5.25

Page 147: C#   classes

This is a perfect example of a master-detail relationship. What follows next, is an explanation of the code that implements this parent-child relationship. At the outset, the DataGrid must be populated with data from a database. At times, it may take considerable time to fetch this data. While this process is on, we would obviously not want the user to interact with or use the control. In order to forbid interference by the user while the data is being retrieved, the DataGrid control provides us with two functions, viz., BeginInit and EndInit. The function BeginInit informs the DataGrid that initialization has begun and the function EndInit signals that the process of data retrieval has been accomplished. The EndInit function is placed at the absolute end of the code handling the database. If we comment out the EndInit function, the DataGrid behaves similarly, but with one small difference, i.e. it now assumes a read-only state. The DataSource property is an object that is normally derived from DataSet. In our program this property is initialized to c, which is an instance of class ccc. Class ccc is derived from DataSet. The constructor of this class performs many interesting actions. We begin by creating an object tc, which is an instance of class cus. This class in turn, is derived from class DataTable. In the constructor of class cus, the parameter 'Customers' is supplied to the constructor of the DataTable, using the base keyword. Thus, the DataTable is now called Customers. We also create a DataColumn object cID, which is named CustomerID, and we use the Add function in the Columns collection of the DataTable class, to add this column to the Customers DataTable. Every table should essentially consist of one or more columns, which can uniquely identify a row of the table. This set of columns is called a Primary Key. The PrimaryKey property is set to an array of DataColumn objects, which constitute the columns comprising the primary key. Normally, the primary key is a single column. Most modern databases would not create a table without first coercing the user to specify the primary key. In this program, the setting of the PrimaryKey property is optional. However, creating the column called CustomerID, is mandatory. Now that we have created a DataTable object, we need to add it to the DataSet. This is achieved by employing the Add function in the Tables collection. An instance of class Orders that is derived from class DataTable, is created and named as Orders. This table has one column with the same name CustomerID. The Orders table is then added to the DataSet. You may note that this keyword is optional. The very quintessence of the application is the DataRelation object. During the creation of the object r, the constructor of the DataRelation object is called with three parameters. The first parameter is the name of the data relation. A name is to be provided, whenever more than one entity of the same type is created. In this case, the name custord becomes a hyperlink, and gets displayed whenever we click on the plus sign. The next two parameters are the parent and child columns, which are related to each other. The column CustomerID from the Customers table is related to the CustomerID column in the Orders table, in order to establish a one-to-many relationship. So, we specify these DataColumn objects as the next two parameters. The DataRelation object can also be related with a DataColumn object, expressed as an array, in case there are multiple DataColumn objects within the relation. The commented line displays the same relation expressed as an array of DataColumn objects. We finally add this freshly minted Relation object to the Relation collection. It is obligatory to derive from the DataSet class, since both the DataTable objects, Customers and Orders, need to be added. Since a DataRelation had to be created between two columns, one each from these tables, we had to create two Data Column objects in classes derived from the DataTable class. This proves that data has not been physically added to the DataTable. Presently, the DataTable comprises of only two columns and a relation. The utility of the second parameter to the Fill function, which is the name of the mapping table, will now become apparent. At this juncture, we associate the data from the database with the tables Customer and Order, through the SqlDataAdapter objects Cust and Ord. As the DataRelation is created through these tables, the DataGrid displays the plus sign. The second parameter to the Fill function is called the Mapping Tables parameter. This parameter is needed, unless we wish to build the relations in the database on our own, which is, by all odds, not a straightforward task. If the AllowNavigation property is set to False, the links to the child tables shall no longer be visible. This program clearly demonstrates how we can impose our own relationships on data contained in databases, which have been created by others.

a.csusing System;using System.Data;using System.Drawing;using System.Windows.Forms;using System.Data.SqlClient;public class zzz : Form {StatusBar s;CustomersDataSet customersDataSet1;

Page 148: C#   classes

Button b;DataGrid d;public zzz() {d = new DataGrid();s = new StatusBar();customersDataSet1 = new CustomersDataSet();b = new Button();d.BeginInit();d.PreferredRowHeight = 16;d.Size = new System.Drawing.Size(584, 336);d.DataSource = customersDataSet1;d.DataMember = "Customers";d.ForeColor = System.Drawing.Color.Navy;d.Location = new System.Drawing.Point(8, 8);d.BackColor = System.Drawing.Color.Gainsboro;d.AlternatingBackColor = System.Drawing.Color.WhiteSmoke;AcceptButton = b;ClientSize = new Size(600, 413);s.BackColor = System.Drawing.SystemColors.Control;s.Size = new System.Drawing.Size(600, 16);s.Text = "Click on Load";s.Location = new System.Drawing.Point(0, 397);customersDataSet1.DataSetName = "CustomersDataSet";b.FlatStyle = FlatStyle.Flat;b.Size = new Size(112, 32);b.Text = "&Load";b.Location = new Point(480, 352);b.Click += new System.EventHandler(abc);Controls.Add(s);Controls.Add(b);Controls.Add(d);d.EndInit();}void abc(object sender, System.EventArgs e) {Cursor cu = Cursor.Current;try {Cursor.Current = Cursors.WaitCursor;SqlConnection con = new SqlConnection("server=(local)\\NetSDK;uid=QSUser;pwd=QSPassword;database=northwind");SqlDataAdapter Cust = new SqlDataAdapter ("Select * from Customers", con);SqlDataAdapter Ord = new SqlDataAdapter ("Select * from Orders", con);SqlDataAdapter OrdD = new SqlDataAdapter ("Select * from [Order Details]", con);s.Text ="Loading Customers...";Cust.Fill(customersDataSet1, "Customers");s.Text ="Loading Orders...";Ord.Fill(customersDataSet1, "Orders");s.Text ="Loading Order Details...";OrdD.Fill(customersDataSet1, "Order_Details");s.Text ="Updating Grid...";} finally {s.Text ="Done";Cursor.Current = cu;}}public static void Main() {Application.Run(new zzz());}}public class CustomersDataSet : DataSet {Customers tc;Orders to;Order_Details td;DataRelation ro;DataRelation rd;public CustomersDataSet() {

Page 149: C#   classes

tc = new Customers("Customers");Tables.Add(tc);to = new Orders("Orders");Tables.Add(to);td = new Order_Details("Order_Details");Tables.Add(td);ro = new DataRelation("CustomersOrders", new DataColumn[] {tc.cID}, new DataColumn[] {to.columnCustomerID}, false);Relations.Add(ro);rd = new DataRelation("OrdersOrder_Details", new DataColumn[] {to.columnOrderID}, new DataColumn[] {td.odID}, false);Relations.Add(rd);}}public class Customers : DataTable{public DataColumn cID;public Customers(string name) : base(name) {cID = new DataColumn("CustomerID");Columns.Add(cID);}}public class Orders : DataTable{public DataColumn columnOrderID;public DataColumn columnCustomerID;public Orders(string name) : base(name) {columnOrderID = new DataColumn("OrderID ", typeof(int));Columns.Add(this.columnOrderID);columnCustomerID = new DataColumn("CustomerID", typeof(string));Columns.Add(this.columnCustomerID);}}public class Order_Details : DataTable{public DataColumn odID;public Order_Details(string name) : base(name) {odID= new DataColumn("OrderID", typeof(int));Columns.Add(odID);}}

On executing the program, we witness an empty DataGrid with a star symbol and the word 'CustomerID' displayed. The status bar perceptibly advises us to click on the button labeled 'Load'.

Screen 5.26 Screen 5.27 After we have done so, a list of customers is displayed, along with a plus sign. If we click on the plus sign, a hyperlink on CustomerOrders will be displayed. A click on this link, would lead to a list of orders placed by this customer. There has been an addition to the program. A plus sign is displayed with every order, which expands to a hyperlink OrdersOrder detail. If we click on this hyperlink, the actual items included in the particular order, are to be displayed.

Page 150: C#   classes

Screen 5.28 Screen 5.29 The first line of the DataGrid also reveals both, the customer details, as well as, the order details. If we click just once on the Back button, it takes us back to the orders. Clicking on it again, takes us further back to the list of customers. We add a StatusBar, as is customary in most applications, to display user interface messages. The constructor of the class ccc, performs the same tasks as explained in the earlier program. Previously, we had created two tables; but now, we create three tables, i.e. Customers, Orders and Order_Details. The Customers table has one column called CustomerID; the Orders table has two columns named CustomerID and OrderID; and the Order_Details table has only one column called OrderID. We now create two relations. The first one is called CustomersOrders that relates the CustomerID columns in the two tables, as before. The second relation is named OrdersOrder_Details, which relates the OrderID column from the Orders table, to the OrderID column from the OrderDetails table. Thus, the only difference between this program and its predecessor is, the inclusion of an additional table, column and relation. The DataSource that represents a DataSet is made up of three tables. The question that is expected to surface in our minds is: Which of the three tables should be used in the initial display? The DataSource property decides the initial table. Since we have specified Customers, we see only one column, i.e. CustomerID. If we comment out the line d.DataMember = "Customers", only a plus sign would be displayed without any column name. Clicking on the plus sign would demonstrate the list of three tables. Finally, when we click on the link that displays Orders, we shall spot the two columns that we have created.

Screen 5.30 Screen 5.31 The property DataSetName can be ignored for the moment. If we click on the button, the function abc gets called. In this function, we first save the current cursor in a Cursor object called cu. This current cursor is stored in the property Current of the Cursor object. Then, we modify the current cursor to the Wait Cursor, since it is extremely time consuming to write the code, in order to retrieve data from the database. We use the Fill function in the similar manner as used before, and then change the text displayed in the Status Bar, depending upon the table that is being filled up from the database. It is advisable to place database-handling code in a try catch statement for error handling. Code placed in the finally clause, resets user interface widgets like the cursor, status bar, etc. We can have as many data relations as we yearn for, and we can build as many logical relations between tables as we covet. In the above program, we have two levels; but surely, many more levels are realizable !

6 Printing

Page 151: C#   classes

One facility that every programmer in the world would want to provide through his program is that of printing. The .NET world has bestowed about ten important printer classes that ease our job of printing text and graphics to the printer. We shall grapple with some small programs to grasp the concepts of printing and conclude this chapter with a small word processor program that is provided along with the .NET samples. You should create a text file named a.txt in the current subdirectory, and thereafter, insert the following three lines:

a.txthi, how are youbye, take careend

a.csusing System;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {Font f;StreamReader sr;public void abc(){sr = new StreamReader ("a.txt");f= new Font("Courier New", 14);PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler(pqr);pd.Print();}void pqr(object o, PrintPageEventArgs e) {float lpp = e.MarginBounds.Height / f.GetHeight(e.Graphics) ;int c = 0 ;String s=null;while (c < lpp && ((s=sr.ReadLine()) != null)) {float y = e.MarginBounds.Top + (c * f.GetHeight(e.Graphics));e.Graphics.DrawString (s, f, Brushes.Black, e.MarginBounds.Left, y);c++;}if (s != null)e.HasMorePages = true ;elsee.HasMorePages = false ;}public static void Main() {zzz a = new zzz();a.abc();}}

The above program prints the contents of the text file a.txt to the default printer. In all our programs in this chapter, we shall create an object that is an instance of class zzz and call function abc off it. The code within this function will execute the actual task of printing. We first create an object, sr, which is an instance of the class StreamReader. The constructor of this class is furnished with the name of the file that we want to print, i.e. a.txt. The StreamReader class is derived from the class TextReader, which reads characters from a stream of bytes that have been encoded in a particular format. The TextReader class belongs to the System.IO namespace. The Stream class too forms a part of the same genre. This class is designed for reading byte streams. The StreamReader class is optimised for reading lines of data from an ASCII or a text file. By default, the encoding used is UTF-8, but it can be changed to any other encoding format, if required. The UTF-8 encoding can comprehend Unicode characters. The StreamReader class, by default, is not thread safe, and neither does it belong to the Printing classes in the .NET family. The class is used in this program to read the file, one line at a time. We want the printer to print our text file in a specific font. So, we create a Font object f, whose constructor is passed the Font Name 'Courier New' along with the size of '14 points'. We have plenty of choice of fonts from the innumerable fonts available. For the neophytes of the publishing world, it would be a revelation that 72 points makes an inch. If we specify a font name such as 'Vijay Mukhi', which does not exist, the printer uses the default font instead, which in this case, is Microsoft Sans Serif. Thus, the printer always receives a valid font type to work with. We create one more object called pd, in our program, which is an instance of class PrintDocument. This class is the nucleus of all printing activities in the .NET world. It has an event called PrintPage, which requires an instance of a delegate

Page 152: C#   classes

PrintPageEventHandler. The delegate represents a method called pqr, which handles the PrintPage event. This event is generated by the Print function. Thus, when we call the Print function, it in turn, calls the pqr function. This function is passed two parameters. The second parameter is a PrintPageEventArgs class, which carries data that is useful for printing. The PrintPageEventArgs class is derived from class EventArgs. The pqr function is not called explicitly, since this would entail creation of an object like PrintPageEventArgs. The Print function is a blocking function. This implies that not a single line of code will be executed after the Print function has been called, until the pqr function completes execution. If the pqr function goes into an indefinite loop, the code succeeding the Print function will never get executed.As mentioned earlier, the second parameter e of data type PrintPageEventArgs contains the data that is to be printed. The member MarginBounds, which is of data type Rectangle, specifies the rectangular portion of the page, which falls within the margins. In our case, the various values of the Rectangle structure are as follows:

Height = 900, Width = 650, Left = 100, Right = 750Top = 100, Bottom = 1000, X = 100, Y = 100.

Before we initiate printing, we need to identify the number of lines that can be printed on a single page. To estimate this, we use the following calculation:

• Height of our page in pixels divided by the height of the font in pixels. In this example, it works out to 900 / 22.02691 = 40.85. This value is stored in the variable lpp, which signifies 'lines per page'. We then use another variable c, which has an initial value of zero. It is incremented in the while loop, once for every line that is printed. As soon as the value of c exceeds that of lpp, the program quits out of the 'while' loop. The ReadLine function of the StreamReader class fetches a new line from the file a.txt and stores it within a string variable s. If there are no more lines to be read from the file, this function returns a null. Thus, the while loop terminates when the number of lines printed exceed the total number of lines that can fit on a page or when there are no more lines to be read from the file. A check must be performed to verify whether we have printed the maximum possible lines that can fit on a page, before we read the next line from the file. This is to forestall reading a line from the file and thereafter, realizing that it cannot be printed on the page due to lack of space. To perform this check, we determine the y position of each line on the page. All text is printed below the Top margin. So to calculate the y position of a line, we use the following calculation:(Height of the font used MULTIPLIED BY the number of lines printed so far) PLUS value of the Top margin. In this case,

The value of the top margin is 100The height of the font is 22.02691

In the case of the first line to be printed, the number of lines printed so far is 0. So, the value of the y co-ordinate for the first line is

=100+(22.02691 X 0) = 100. The value of the y co-ordinate for the second line is

=100+(22.02691 X 1) = 122.0269. The value of the y co-ordinate for the third line is

=100+(22.02691 X 2) = 144.0538. We accessed these values using the WriteLine function. The Graphics member in class PrintPageEventArgs is employed to write to the printer. This member contains the function DrawString. This function, which we had used earlier to write to the screen, is now utilized to write to the printer. The DrawString function accepts 5 parameters:

1) A string, s2) A font, f3) A color or brush, Black4) The left margin for the x coordinate, MarginBounds.Left5) The y co-ordinate

As you may have noticed, the same DrawString function is used for printing to the printer and writing to the screen. Abstraction, therefore, enables us exploit and reuse concepts that we have learnt earlier. The while loop ends when any of the following two conditions is satisfied:

• The number of lines printed exceeds the lines per page i.e. when we need to print on the next page. • The file has no more lines to print.

To figure out which of the above conditions has ended the 'while' loop, we verify the value contained in string s. If the value is null, it signifies that there are no more lines to print.

Page 153: C#   classes

As soon as we exceed the maximum number of lines that can be printed on a page, and there are lines remaining to be printed, there has to be a mechanism to inform the PrintDocument class that the function pqr needs to be called again, to print the next page. To accomplish this, the HasMorePages member of the PrintPageEventArgs object e is either set to true or false. If it is set to true, the function pqr gets called again.

a.csusing System;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {Font f;public void abc(){f= new Font("Courier New", 14);PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler(pqr);pd.PrintPage += new PrintPageEventHandler(xyz);pd.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Vijay Mukhi", f, Brushes.Black, 100, 200);}void xyz(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", f, Brushes.Black , 200, 300);}public static void Main() {zzz a = new zzz();a.abc();}}

Through this example, we once again demonstrate the power of events and delegates. We attach two methods, pqr and xyz, with the printing event. The first function pqr prints 'Vijay Mukhi' at X=100 and Y=200 and then, the method xyz prints 'Sonal Mukhi' at X=200 and Y=300. The HasMorePages property is initialized to false by the program, even though it has the value of false by default. Both these lines get printed on the same page. Each time we set HasMorePages to true, the .NET framework sends a 'form feed' signal to the printer that sets up a new page for printing. The only mechanism of printing text to a printer is by means of the Graphics object. If the 'font' and the 'Brushes' parameters are set to null, a run-time exception is generated. Hence, they must be explicitly specified. Unlike a dot matrix printer that prints one line at a time, a laser printer first creates an entire page in memory and then sends it for printing. Thus, in the function pqr, if we change the Y coordinate from 200 to 400, both lines get printed on the same page, since the page first gets created in the memory, and then it is sent to the printer. The PrintDocument class creates the PrintPageEventArgs object, since that is the sole mechanism of activating the printer. Then, it calls all the methods registered through the delegate.

a.csusing System;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){yyy y = new yyy(); y.PrintPage += new PrintPageEventHandler(pqr);y.Print();}void pqr(object o, PrintPageEventArgs e) {System.Console.WriteLine("pqr");e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();

Page 154: C#   classes

a.abc();}}public class yyy : PrintDocument{Font f;protected override void OnQueryPageSettings(QueryPageSettingsEventArgs e){base.OnQueryPageSettings(e) ;System.Console.WriteLine("OnQueryPageSettings");}protected override void OnBeginPrint(PrintEventArgs e) {base.OnBeginPrint(e) ;System.Console.WriteLine("OnBeginPrint");f = new Font("Courier New",14);}protected override void OnEndPrint(PrintEventArgs e) {base.OnEndPrint(e) ;System.Console.WriteLine("OnEndPrint");}protected override void OnPrintPage(PrintPageEventArgs e) {base.OnPrintPage(e) ;System.Console.WriteLine("OnPrintPage");e.Graphics.DrawString ("Vijay Mukhi", f, Brushes.Black, 100, 400);}}

OutputOnBeginPrintOnQueryPageSettingspqrOnPrintPageOnEndPrint

In the above example, class yyy is derived from PrintDocument. Thus, all code embodied in the PrintDocument class is available to yyy. This also implies that any code in the class can be overridden. The Print method of the PrintDocument class had earlier called the methods registered with the delegate, whereas now, it calls a series of methods from the PrintDocument class. In the situation where we want to call our methods with the same name as in class yyy, the keyword 'override' has to be used. The output confirms that the first function to be called is OnBeginPrint. This function is akin to an initialization function. All code that is to be executed only once must be placed in this function. It is a sage programming practice to always call the original function from the base class. Thus, we use the keyword base to call the original OnBeginPrint from the class PrintDocument. Superior programming style decrees that, initially, the base functions should be called. It is not mandatory to do so, since even if this is not done, the class still behaves in a similar manner. The documentation too is silent on this issue, but we consider it prudent to do so and advise you also to call the original function first. The parameter PrintEventArgs passed to the function has no significant role to play in this release. The documentation clearly states that this PrintEventArgs has been designed for use in a future release. Thus, it cannot be put to any consequential use at present. We create a Font object in this function, since it is a one-time activity. The second function that gets called is OnQueryPageSettings, which will be elucidated in the next example. Following it, function pqr gets called, which facilitates printing. Then, function OnPrintPage gets called with the same parameters as that of the function pqr. If we so desire, we can print in the delegate method. Other than the delegate method being called first, there is no apparent difference. Finally, the function OnEndPrint is called, in which, we are presented with the opportunity to clean up any resources created in the method OnBeginPrint.

a.csusing System;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){

Page 155: C#   classes

yyy y = new yyy(); y.PrintPage += new PrintPageEventHandler(pqr);y.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();a.abc();}}public class yyy : PrintDocument{Font f;protected override void OnQueryPageSettings (QueryPageSettingsEventArgs e){base.OnQueryPageSettings(e) ;e.PageSettings.Landscape = true;}protected override void OnBeginPrint(PrintEventArgs e) {base.OnBeginPrint(e) ;f = new Font("Courier New",14);}protected override void OnPrintPage(PrintPageEventArgs e) {base.OnPrintPage(e) ;e.Graphics.DrawString ("Vijay Mukhi", f, Brushes.Black, 100, 400);}}

Each time we use the printer, we may be desirous of customizing the print settings for each page. To enable this, the framework calls the function OnQueryPageSettings before printing any page. In this function, we can change the page settings using the parameter e, which is of type QueryPageSettingsEventArgs. This class is derived from PrintEventArgs class. One of the members in QueryPageSettingEventArgs is PageSettings, which contains a large number of properties that control the printer settings. Here, we have only used the Landscape property and assigned it a boolean value of true. Had we assigned it a value of false, the property would have been set to Portrait mode, which is usually the default mode. Other properties that can be modified are: the paper tray i.e. the source from where the paper is fed to the printer, the printer resolution, the page margins, the color etc. Any property of a printer that can be set manually can now be done using this function.

a.csusing System;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){yyy y = new yyy(); y.PrintPage += new PrintPageEventHandler(pqr);y.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();a.abc();}}public class yyy : PrintDocument{Font f;

Page 156: C#   classes

protected override void OnPrintPage(PrintPageEventArgs e) {base.OnPrintPage(e) ;e.Cancel = true;f = new Font("Arial",13);e.Graphics.DrawString ("Vijay Mukhi", f, Brushes.Black, 100, 400);}}

In this program, the Cancel property of PrintPageEventArgs is set to true, resulting in cancellation of the print job. The printing that was to be carried out by the delegate function also gets cancelled. All print jobs are aborted. The PageSettings object used in the earlier program is also a property of the PrintPageEventArgs class, and thus, can be used in this function too.

a.csusing System;using System.Windows.Forms;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){PrintDocument y = new PrintDocument (); y.PrintPage += new PrintPageEventHandler(pqr);PrintDialog d = new PrintDialog() ;d.Document = y;DialogResult r = d.ShowDialog();if (r == DialogResult.OK) y.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();a.abc();}}

In this example, we would like to display a dialog box to the user where he can change the printing options. Therefore, firstly we create an object d as an instance of class PrintDialog. Creating a PrintDialog object assigns default values to the following eight properties:

• AllowSomePages is set to False, disabling the Pages option button. • AllowSelection is set to False, disabling the Selection option button. • AllowPrintToFile is set to True, enabling Print to File check box. • Document, which is a reference to the PrintDocument object, is set to null. • PrinterSettings is assigned the value of False, disabling the dialog box.• PrintToFile decides whether the printer would print to disk or to the printer. If the value is null, it prints to the printer. • ShowHelp is set to False, thus disabling the help button.• ShowNetwork is set to True, thus enabling the network button.

We can modify any of these values to befit our requirements. We can also select specific parts of the document to print.

Screen 6.1

Page 157: C#   classes

When we run the program, the normal Windows Printer Dialog box is displayed, allowing us to modify any of the properties. The ShowDialog function facilitates the display of the dialog box. This function waits until one of two the buttons, OK or Cancel, is clicked.The value returned on clicking one of these two buttons is stored in the DialogResult object r. If the OK button is selected, it will start the printing, whereas if the Cancel button is selected, it will cancel or abort the printing. When the OK button is selected, the function ShowDialog returns an enum DialogResult, with a value OK. The above enum can have as many as 8 different values, depending upon the type of buttons available in the Dialog Box. Thereafter, the Print function of the PrintDialog class is called. One of the final actions of the function ShowDialog is to initialize the Document property supplied by PrintDocument object, i.e. y, to the printer settings chosen in the Dialog box.

a.csusing System;using System.Windows.Forms;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){PrintDocument y = new PrintDocument (); y.PrintPage += new PrintPageEventHandler(pqr);PageSetupDialog p = new PageSetupDialog () ;PageSettings s = new PageSettings();p.PageSettings = s ;p.ShowDialog ();y.DefaultPageSettings = s; y.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();a.abc();}}

In the above example we usher in a new class, PageSetupDialog, which allows the user to change page settings such as the margins and the paper orientation. When we create a PageSetupDialog object, properties in the object get initialized to their default values. Some of the properties are :

• AllowMargins: set to true, enables us to change the margins settings.• AllowOrentation: set to true, enables the Portrait/Landscape radio buttons.• AllowPaper: set to true, so that we can choose the paper size and source.• AllowPrinter: set to true, enables the printer button.• MinMargins: set to null, so that the default margins of 1 inch are displayed.• PageSettings: set to null.• PrinterSettings: set to null.

It is mandatory to initialise the PageSettings property to an actual object containing the settings selected by the user. Finally the function ShowDialog initializes the object s.

Screen 6.2

Page 158: C#   classes

Then, the property DefaultPageSettings of the PrintDocument class is initialised to the PageSettings object s, so that the printing framework uses the settings stored in this property. Thus, the two dialog boxes have their own unique purposes, and each of them fills up an object that is used by the PrintDocument class. In the Windows world, Dialog boxes are used to fill up properties/variables in an object. In the above two programs, the function pqr does not receive any values from the dialog boxes.

a.csusing System;using System.Windows.Forms;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler(pqr);PrintPreviewDialog dlg = new PrintPreviewDialog() ;dlg.Document = pd;dlg.ShowDialog();pd.Print();}void pqr(object o, PrintPageEventArgs e) {e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 200);}public static void Main() {zzz a = new zzz();a.abc();}}

Screen 6.3 Before we buy goods, we often like to preview them. The same analogy is true when we want to print a document. The class PrintPreviewDialog enables us to preview a document before printing it. The only property that is to be initialized here is Document property. This is mandatory since there is no other mechanism by which the Dialog box can receive the contents of the page. We can safely assume that the ShowDialog function calls Print, which figures out the text to be printed on a page. It also has the ability to display the content in different magnification percentages and in multiple pages at a time. The Dialog contains a PrintPreviewControl, which has several members that can be toyed around with, in order to transform the appearance.

a.csusing System;using System.Windows.Forms;using System.Drawing;using System.Drawing.Printing;using System.IO;public class zzz {public void abc(){PrintDocument pd = new PrintDocument(); pd.PrintPage += new PrintPageEventHandler(pqr);

Page 159: C#   classes

pd.Print();}void pqr(object o, PrintPageEventArgs e) {Image i = Image.FromFile("Sample.jpg");Point p = new Point(100, 100);e.Graphics.DrawImage(i, p);e.Graphics.DrawString ("Sonal Mukhi", new Font("Courier New",10), Brushes.Black, 100, 600);}public static void Main() {zzz a = new zzz();a.abc();}}

A printer has the capability of not only printing text, but also lines, ellipses, curves and images. This program prints an image. Before you proceed, ensure that file sample.jpg resides in the current directory. The Image class constructor is supplied with a jpg file, which we have picked up from the .NET samples. The Point object p represents the X, Y coordinates where the image is to be drawn. Then, using the DrawImage function from the Graphics class, the image is printed at the position specified. Using this procedure, all images displayed on the screen in the previous chapters can be sent to the printer for printing.

7 XML Classes eXtensible Markup Language i.e. XML is a subset of the Standard Generalized Markup Language (SGML), which is an ISO standard numbered ISO 8879. SGML was perceived to be remarkably colossal and extremely convoluted to be put to any pragmatic use. Thus, a subset of this language, XML, was developed to work seamlessly with both SGML and HTML. XML may be considered as a restricted form of SGML, since it conforms to the rules of an SGML document. XML was created in the year 1996 under the auspices of the World Wide Web Consortium (W3C), under the chairmanship of Jon Bosak. This group spelt out 10 ground rules for XML, with 'ease of use' as its fundamental philosophy. From thereon, the expectations reached a threshold wherein, XML was expected to eradicate world poverty and generally rid the world of all its tribulations. To be precise, XML was overvalued, way beyond realistic levels. There are people who appear to be extremely infatuated by XML, even though they may not have read through a single rule or specification of the language. The specifications of XML laid down by its three primary authors- Tim Bray, Jean Paoli and C. M. Sperberg-McQueen, are accessible at the web site http://www.w3.org/XML. XML documents consists entities comprising of Characters or Markups. An XML file is made up of a myriad components, which shall be unravelled one at a time, after we have discerned the basic concepts of this language. We commence this chapter by introducing a program that generates an XML file.

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.Flush();a.Close();}}

In this program, we use a class called XmlTextWriter, which comes from the System.Xml namespace. An instance 'a' of the XmlTextWriter class is created, by passing two parameters to the constructor:

• The first parameter, b.xml, is a string and represents the name of the file to be created. If the file exists in the current directory, it gets deleted and then recreated, but with zero bytes. • The second parameter is null. It represents the Encoding type used.

Unicode is a standard whereby each character is assigned 16 bits. All the languages in the world can now be easily represented by this standard. In the .Net world, we are furnished with classes whose methods facilitate conversion of arrays and strings made up of Unicode characters, to and from arrays made up of bytes alone.

Page 160: C#   classes

The System.Text namespace has a large number of Encoding implementations, such as the following: • The ASCII Encoding encodes the Unicode characters as 7-bit ASCII.• The UTF8 Encoding class encodes Unicode characters using UTF-8 encoding.

UTF-8 stands for UCS Transformation Format 8 bit. It supports all Unicode characters. It is normally accessed as code page 65001. UTF-8 is the default value and represents all the letters from the English alphabet. Here, since we have specified the second parameter as null, the default value of UTF-8 encoding is taken. If we execute the program at this stage, you would be amazed by the fact that no file by the name of b.xml will be displayed. To enable this to happen, a function named Flush needs to be called. Each time we ask the class XmlTextWriter to write to a file, it may not oblige immediately, but may place the output in a buffer. Only when the buffer becomes full, will it write to the file. This approach is pursued to avoid the overhead of accessing the file on the disk repetitively. This improves efficiency. The Flush function flushes the buffer to the file stream, but it does not close the file. The Close function has to be employed to execute the twin tasks of flushing the buffer to the file, and closing the file. It is sagacious to call Flush, and then call Close, even though Close is adequate to carry out both these tasks.

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Flush();a.Close();}}

b.xml<?xml version="1.0"?>

Here, we have called a function called WriteStartDocument from the XmlTextWriter class, which does not take any parameters. It produces the line <?xml version="1.0"?>, in the file b.xml. Any line that begins with <?xml is called an XML declaration. Every entity in XML is described as a node. Every XML file must begin with an XML Declaration node. There can be only one such node in our XML file and it must be placed on the first line. Following it is an attribute called version, which is initialized to a value of 1.0. The XML specifications lucidly stipulate that there would be no attribute called version in the next version of the software. Even if there is, its value would be indeterminate. In other words, in the foreseeable future, the only mandatory attribute would be version=1.0.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.WriteDocType("vijay", null, null ,null);a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay>

The next vital declaration is the DOCTYPE declaration. Every XML file must have one DOCTYPE declaration, as it specifies the root tag. In our case, the root tag would be 'vijay'. An XML file is made up of tags, which are words enclosed within angular brackets. The file also contains rules, which bind the tags. The next three parameters of the function WriteDocType are presently specified as null. You may refer to the documentation to decipher the remaining values, since these may be used in place of null. If this does not appeal to you, you may have to hold your horses, till we furnish the explanation at an appropriate time.

a.csusing System;using System.Xml;public class zzz

Page 161: C#   classes

{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay>

In the earlier example, all the nodes were displayed on the same line. We would indubitably desire that every node be displayed on a new line. The property Formatting in XmlTextWriter, is used to accomplish this task. Formatting can be assigned only one of the following two values: Indented or None. By default, the value assigned is None. The Indented option indents the child elements by 2 spaces. The magnitude of the indent may be altered, by stipulating a new value for the Indentation field. In our program, we want the indent to be 3 spaces deep. Hence, we stipulate the value as 3. As is evident, all nodes do not get indented. For example, the DOCTYPE node does not get indented; instead, it is placed on a new line. The IndentChar property may be supplied with the character that is to be employed for indentation. By default, a space character is used for this purpose.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay />

The function WriteStartElement accepts a single parameter, which is the tag name, to be written to the XML file. This is an oft-repeated instruction, to be iterated in almost every program, since an XML file basically comprises of tags. A tag normally has a start point and an end point, and it confines entities within these two extremities. However, there are tags that do not accept any entities. Such tags end with a / symbol.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("wife","sonal");a.Flush();a.Close();}

Page 162: C#   classes

}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay wife="sonal" />

The newly added function WriteAttributeString accepts two parameters, which it writes in the form of a name-value pair. Thus, along with 'vijay', we see the attribute named 'wife', having a value of 'sonal'. An attribute is analogous to an adjective of the English language, in that, it describes the object. In our case, it describes the tag 'vijay'. It divulges additional information about the properties of a tag. XML does not interpret the contents of these tags. The word 'wife' or the value 'sonal', have no special significance for XML, which is absolutely unconcerned about the information provided within the tags.

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("wife","sonal");a.WriteElementString("surname", "mukhi");a.Flush();a.Close();}}b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay wife="sonal"> <surname>mukhi</surname></vijay>

An element represents entities within a tag. We have a tag surname containing the value 'mukhi'. We can have multiple tags within the root tag. We have been reiterating the fact that we need to adhere to specific rules. You may steer clear of the beaten path and interchange the following two newly added functions as follows:

a.WriteElementString("surname", "mukhi");a.WriteAttributeString ("wife","sonal");

As a fallout of this interchange, the following exception will be thrown:

Unhandled Exception: System.InvalidOperationException: Token StartAttribute in state Content would result in an invalid XML document.

This exception is triggered off due to the fact that the attribute must be specified first. Then, and only then, should the child tags within the tag, be specified.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("wife","sonal");a.WriteAttributeString ("friend","two");a.WriteElementString("surname", "mukhi");

Page 163: C#   classes

a.WriteElementString("books", "67");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay wife="sonal" friend="two"> <surname>mukhi</surname> <books>67</books></vijay>

To summarize, the WriteDocType function specifies the root tag, the WriteStartElement the tag, the WriteAttributeString, the attributes for the active tag and WriteElementString function, a tag within a tag. We can enumerate as many attributes as we desire. They will eventually be clustered together. The WriteElementString function is also capable of creating as many tags, as are needed under a tag. In the file b.xml, we see two attributes and two tags, under the root tag 'vijay'.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("friend","two");a.WriteStartElement("mukhi");a.WriteAttributeString ("wife","sonal");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay friend="two"> <mukhi wife="sonal" /></vijay>

In the above example, 'vijay' is the root tag, with the attribute 'friend', which is assigned a value of 2. It also has a child tag 'mukhi' having the attribute of 'wife' initialized to 'sonal'. Both the tags, 'vijay' and 'mukhi', are created using the function WriteStartElement. Unlike function WriteElementString, which creates a start and end tag, WriteStartElement creates only a start tag. A tag too can be endowed with attributes. The active tag is the last inserted by the WriteStartElement function. Functions such as WriteAttributeString, act on the active tag. Thus, we notice that the attribute of 'wife' has the tag 'mukhi' and not 'vijay'. Finally, since the tag 'mukhi' is devoid of any contents, it ends with a / symbol on the same line.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("friend","two");a.WriteStartElement("mukhi");

Page 164: C#   classes

a.WriteAttributeString ("wife","sonal");a.WriteFullEndElement();a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay friend="two"> <mukhi wife="sonal"> </mukhi></vijay>

The function WriteFullEndElement marks the end of the active tag. Therefore, the single tag 'mukhi', does not end with a / symbol on the same line. It has an ending tag instead. Both these possibilities are equally valid in this case. But, if the tags embody any contents, then both the start and the end tags are mandatory. In such situations, a single empty tag would just not suffice.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);//a.WriteComment("comment 1");a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteStartDocument();a.WriteComment("comment 1");a.WriteDocType("vijay", null, null ,null);a.WriteComment("comment 2");a.WriteStartElement("vijay");a.WriteAttributeString ("wife","sonal");a.WriteComment("comment 3");a.WriteElementString("surname", "mukhi");a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!--comment 1--><!DOCTYPE vijay><!--comment 2--><vijay wife="sonal"> <!--comment 3--> <surname>mukhi</surname></vijay>

Every programming language extends the facility of writing comments, even though it may be a seldom used feature. Programmers insert comments amidst their code to document or explain the functioning of their programs. At times, comments assist in deciphering the code from the programmer's perspective. Practically, it may be easier to teach an elephant how to tap-dance, than to convince a programmer to write comments. In the XML world, comments begin with <!-, and end with -->. This is somewhat similar to the HTML syntax. In fact, the rules of HTML are written in XML. Comments are like a liquid, since they can be moulded to fit-in anywhere, except on the first line of a program. The first line in an XML file has to be a declaration. If you dispense with the comments given with the function WriteComment, an exception will be thrown with the following message:

Unhandled Exception: System.InvalidOperationException: WriteStartDocument should be the first call.

Thus, functions such as WriteComment, can be used to insert comments anywhere in the code, primarily for the purpose of documentation, which would enable even an alien from outer space to decipher the code better.

a.cs

Page 165: C#   classes

using System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteProcessingInstruction ("sonal", "mukhi=no");a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay> <?sonal mukhi=no?></vijay>

A line beginning with <?, Is called a Processing Instruction (PI). This line is inserted using the function WriteProcessingInstruction, and is passed two parameters:

• the first is the name of the processing instruction.• the second is the text that is to be inserted for the processing instruction.

A Procession Instruction is used by XML to communicate with other programs during the performance of certain tasks. XML does not have the wherewithal to execute instructions. It therefore delegates this task to the XML processor. The processor is a program that is able to recognise an XML file. When it encounters the processing instruction, and if it is able to understand it, it executes it. In cases where it cannot comprehend it, the processor simply ignores the instruction. This is the methodology by which XML communicates with external programs. In our program, the instruction 'sonal' is ignored, as it does not provide any meaningful input to the processor.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteString("mukhi");a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay>mukhi</vijay>

An XML file mainly consists of strings and tags. The WriteString function is very extensively exploited, since it writes content/strings between tags. In the above example, the text 'mukhi' is enclosed within the tags of 'vijay'. Even though we have not explicitly asked the XmlTextWriter class to close the tag, the ending tag has been used because there exists some content after the opening tag.

a.csusing System;using System.Xml;public class zzz{public static void Main()

Page 166: C#   classes

{XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString ("friend","two");a.WriteString("hi");//a.WriteAttributeString ("friend","three");a.WriteStartElement("mukhi");a.WriteAttributeString ("friend","two");a.WriteString("bye");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay friend="two">hi<mukhi friend="two">bye</mukhi></vijay>

The function WriteString can be inserted almost anywhere in the program. The first WriteString function writes 'hi' between the tags of 'vijay', while the second WriteString function writes 'bye' between the tags of 'mukhi'. The WriteString is aware of the active tag. Therefore, it inserts the text accordingly. Here also, if we uncomment the line, a.WriteAttributeString("friend","three"), the following exception will be generated.

Unhandled Exception: System.InvalidOperationException: Token StartAttribute in state Content would result in an invalid XML document.

XML is very strict and meticulous in the sense that, it expects a certain order to be maintained, or else, it throws an exception. For instance, an element or a tag has to be created first. Only then, can all the attributes be written; and finally, the text or content has to be supplied. We are not permitted to write the text first and enter the attributes later. In the XmlTextWriter class, there is no going back. It is a one-way path, which only moves in the forward direction.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteCharEntity ('A');a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay>&#x41;</vijay>

During our exploratory journey of XML, we shall discuss a large number of characters that are 'reserved'. They have a special significance and cannot be used literally. These Unicode characters have to be written in a hex format. The function WriteCharEntity performs this task. It accepts a char or a Unicode character as a parameter and returns a number in hex, prefaced with the &# symbol. For those who do not understand hexadecimal and consider it Greek and Latin, 41 hex is equal to ASCII 65, which is the ASCII value for the capital letter A. You can pass different characters to this function and see their equivalent hex values.

a.csusing System;using System.Xml;public class zzz{

Page 167: C#   classes

public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteCData("mukhi & <sonal>");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay><![CDATA[mukhi & <sonal>]]></vijay>

The above program introduces a new function called WriteCData, which creates a node called CDATA. The parameter passed to this function is placed as it is, but is enclosed within square brackets. A CDATA section is used whenever we want to use characters such as <, >, & and the likes, in their literal sense, which would otherwise be mistaken for Markup characters. Thus, in the above program, the CDATA section that contains the symbol &, interprets it as the literal character &, and not as a special character. Also, <sonal> is not recognized as a tag in this section. A CDATA section cannot be nested within another CDATA section.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteString("<A>&");a.WriteCData("<A>&");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay>&lt;A&gt;&amp;<![CDATA[<A>&]]></vijay>

This program illustrates certain characters that are special to XML. These are the obvious characters, such as <, > and &, since they are used whilst an XML file is being created. Thus, whenever XML comes across the following symbols, it replaces them with the symbols depicted against each:

• < is replaced with '&lt;' • > is replaced with '&gt;' • & is replaced with '&amp;'.

If the same string that contains the above mentioned special characters is placed within a CDATA statement, gets written verbatim, without any conversions.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);

Page 168: C#   classes

a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteEntityRef("Hi");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay>&Hi;</vijay>

The entity ref is very straightforward to understand. The string passed to the function WriteEntityRef is placed in the XML file, preceded by a '&' sign and followed by a semi-colon. An entity ref in XML is equivalent to a variable. It is included to provide flexibility to the program. Thus in the above code, a variable called 'hi' is created. The task of stating what 'hi' signifies, can be defined in the XML file.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteRaw("<A>&");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay><A>&</vijay>

The WriteRaw function writes the characters passed to it, without carrying out any conversions. The above XML file is obviously erroneous, as no end tag has been specified for the tag A. Also, no name has been specified after the & sign.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");Boolean b = true;a.WriteElementString("Logical", XmlConvert.ToString(b));Int32 c = -2147483648;a.WriteElementString("SmallInt", XmlConvert.ToString(c));Int64 d = 9223372036854775807;a.WriteElementString("Largelong", XmlConvert.ToString(d));Single e = ((Single)22)/((Single)7);a.WriteElementString("Single", XmlConvert.ToString(e));Double f = 1.79769313486231570E+308;a.WriteElementString("Double", XmlConvert.ToString(f));DateTime h = new DateTime(2001, 07, 08 ,22, 0, 30, 500);

Page 169: C#   classes

a.WriteElementString("DateTime", XmlConvert.ToString(h));a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay> <Logical>true</Logical> <SmallInt>-2147483648</SmallInt> <Largelong>9223372036854775807</Largelong> <Single>3.142857</Single> <Double>1.7976931348623157E+308</Double> <DateTime>2001-07-08T22:00:30.5000000+05:30</DateTime></vijay>

The above example contains a plethora of data types such as, boolean, int, double and Data Time. The XmlConvert class has a large number of static functions that help us convert one data type to another. One such function is the ToString function. For types such as int or long, the smallest and the largest values are used, in order to check the veracity of the ToString function. The ToString function is overloaded to handle many more data types than we have shown. The point here is that, it is possible for us to convert any data type into a string and write it to disk. This factor gains immense importance when data is being received from a database, and requires to be converted into a string in an XML file.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteStartAttribute("hi", "mukhi", "xxx:yyy");a.WriteString("1-861003-78");a.WriteEndAttribute(); a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay hi:mukhi="1-861003-78" xmlns:hi="xxx:yyy" />

In the above example, we have introduced the WriteStartAttribute function. As is apparent from its name, it starts an attribute. The first parameter to this function is 'hi', which is the namespace, to which the prefix of the attribute belongs. The second parameter 'mukhi' is the name of the attribute. The names assigned to attributes and tags may not always result in a unique name. A programmer may inadvertently create a tag or an attribute with a name that already exists. How then does XML decide what the tag denotes? To help resolve such potential conflicts, each tag or entity is prefaced with a name known as the namespace. This is followed by a colon sign. Normally, meaningful names are assigned, rather than words like 'hi'. Prefixes or namespaces like xmlns, are reserved by XML. The concept of namespaces in XML is identical to the concept of namespaces in C#. The third parameter is a Uniform Resource Identifier (URI). This parameter reveals greater details about the location of the namespace. It informs XML that somewhere within the document, additional information about the namespace 'hi' is available. In this case it is at xxx:yyy. As the WriteStartAttribute function does not specify any value for the attribute, the WriteString function is employed to assign the value 1-861003-78, to the attribute 'mukhi' in the namespace 'hi'.

a.csusing System;using System.Xml;

Page 170: C#   classes

public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString("xmlns", "bk", null, "sonal:wife");string p = a.LookupPrefix("sonal:wife");a.WriteStartAttribute(p, "mukhi", "sonal:wife");a.WriteString("sonal");a.WriteEndAttribute(); a.Flush();a.Close();}} b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay xmlns:bk="sonal:wife" bk:mukhi="sonal" />

Here, the function WriteAttributeString is called with four parameters. The first, as always, is the name of the namespace, i.e. xmlns. The second is the name of the attribute i.e. bk, which is suffixed to the name of the namespace, as xmlns:bk. The third parameter is the namespace URI. In the earlier program, we had specified the value of xxx:yyy for the URI. For this program, since the namespace xmlns is a reserved namespace, the URI parameter is specified as null. The last parameter is the value of the attribute. As a consequence, the above function takes the form of an attribute consisting of xmlns:bk=sonal:wife. The next function LookupPrefix, accepts a namespace URI and returns the prefix. As the parameter supplied to this function is sonal:wife, the prefix returned is bk, which is stored in p. The WriteStartAttribute then uses the following:

• 'bk' as the namespace,• 'mukhi' as the name of the attribute, and• 'sonal:wife' as the namespace URI.

Thus, the attribute 'mukhi' is prefaced with the namespace 'bk'. Finally, the WriteString function assigns the value of 'sonal' to the attribute bk:mukhi.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString("xmlns", "bk", null, "sonal:wife");a.WriteAttributeString("jjj", "bk", "kkk", "sonal:wife");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay xmlns:bk="sonal:wife" jjj:bk="sonal:wife" xmlns:jjj="kkk" />

In this version of the WriteAttributeString function, the namespace is jjj and the attribute name is bk, with the value sonal:wife. Thus, the attribute becomes jjj:bk=sonal:wife. The third parameter to the function is the namespace URI, which is now assigned a value of kkk, instead of null. Thus, one more attribute xmlns:jjj gets added, which indicates that the namespace URI is kkk. We notice that this attribute does not get added for the xmlns namespace. We have chosen the attribute name 'bk' again, just to demonstrate that they belong to different namespaces. Therefore, this bk is considered to be a different attribute from the earlier bk.

Page 171: C#   classes

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteStartAttribute(null,"sonal", null);a.WriteQualifiedName("mukhi", "http://vijaymukhi.com");a.WriteEndAttribute();a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay sonal="n1:mukhi" xmlns:n1="http://vijaymukhi.com" />

In the WriteStartAttribute function, only the second parameter out of the three parameters, has a value 'sonal, which is the name of the attribute. The first parameter, which is the name of the namespace and the third parameter, which is the URI of the namespace, are both assigned null values. The next function, WriteQualifiedName assigns a value to the attribute 'sonal'. This function takes two parameters, the value 'mukhi' and the namespace URI for the value. The value 'mukhi' gets prefaced by a namespace n1, which is created dynamically by XML. The name n1 belongs to the reserved xmlns namespace and the URI to n1 is specified in the second parameter, http://vijaymukhi.com. The method WriteQualifiedName, then looks up the prefix within the scope for the given namespace.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.WriteStartElement("vijay");a.WriteAttributeString("xmlns","mukhi",null,"xxx:yyy");a.WriteString("Hi ");a.WriteQualifiedName("sonal","xxx:yyy");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><vijay xmlns:mukhi="xxx:yyy">Hi mukhi:sonal</vijay>

In this example, we first create an attribute 'mukhi' in the reserved namespace xmlns. This attribute is then rendered a value of xxx:yyy. The WriteString function writes 'Hi' as the content and then, the WriteQualifiedName writes the string 'sonal'. However, since 'sonal' is a Qualified name, it is prefaced by 'mukhi' and not by xxx:yyy, because 'mukhi' is equated to xxx:yyy. The prefix in the scope for the namespace is given precedence.

a.csusing System;using System.Xml;public class zzz{public static void Main() {

Page 172: C#   classes

XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.WriteStartElement("vijay");a.WriteElementString("vijay","mukhi");a.WriteElementString("vijay","sonal","mukhi");a.Flush();a.Close();}} b.xml<?xml version="1.0"?><vijay> <vijay>mukhi</vijay> <vijay xmlns="sonal">mukhi</vijay></vijay>

As we have just observed, the WriteElementString function had only two parameters in the earlier program. However, here it has three parameters. The first and the third parameters are the same, i.e. the tag name and the value. The newly inducted second parameter indicates the namespace 'sonal'. The tag in the first parameter 'vijay', has the namespace of sonal. Thus, the XML file contains the tag with the attribute of xmlns=sonal.

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter (Console.Out);a.WriteStartDocument();a.WriteStartElement("vijay");a.Close();}} Output<?xml version="1.0" encoding="IBM437"?><vijay />

The XmlTextWriter class can write to different entities, using the constructor that accepts a single parameter. The Console class has a static property out of datatype TextWriter that represents the console. Thus, the output is now displayed on the console. By default, the encoding attribute is assigned a value of IBM437. One of the primary reasons for designing XML was to introduce validation of the tags in order to produce a well-evolved XML file. There are a few validations that need to be performed in an XML file, such as:

• It should be ensured that the basic rules of XML as well as our indigenous rules are followed. • Certain tags should be placed only within specified tags and cannot be used independently.• The number of times a tag is being used can be regulated, since it cannot be used infinite times. • A check should be placed on the name and the number of times an attribute is used within a tag.

All such rules that need to be enforced are enunciated in XML parlance and then, placed in a DTD or a Document Type Description. The DTD may either be placed in a separate file or may be made part of the DOCTYPE declaration. In the XML file shown below, the DTD is internal. Thus, a DTD stores the grammar that is permissible in an XML file. The entity refs are also defined in a DTD. One of the reasons why HTML is also reffered to as XHTML is that, the rules of well-formed html are available in the form of a DTD.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;String s = "<!ELEMENT vijay (#PCDATA)>";a.WriteDocType("vijay", null, null, s);a.WriteStartElement("vijay");a.Flush();

Page 173: C#   classes

a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay[<!ELEMENT vijay (#PCDATA)>]><vijay />

The WriteDocType function accepts four parameters. The first parameter is the starting or root tag 'vijay'. Hence, it must contain a value. The last parameter is the subset (as referred to by the documentation), which follows the root tag 'vijay'. If you observe the DOCTYPE statement carefully, you will notice that an extra pair of square brackets [], have been added.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.WriteDocType("vijay", null, "a.dtd", null);a.WriteStartElement("vijay");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay SYSTEM "a.dtd"><vijay />

The third parameter to WriteDocType function specifies the name of the DTD file. In other words, it states the URI of the DTD. The second parameter is assigned the value of null. Hence, the word SYSTEM is displayed before the name of the file, in the XML file. Whenever XML wishes to ensure the validity of an XML file, it ascertains the rules from a.dtd. If both internal and external DTDs are present, both of them are checked. However, the internal DTD is accorded priority.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.WriteDocType("vijay", "mmm", "a.dtd", null);a.WriteStartElement("vijay");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay PUBLIC "mmm" "a.dtd"><vijay />

In the earlier program, SYSTEM was added in the XML file, since the second parameter had been specified as null. However, in this program, the second parameter is not null. Hence, the word PUBLIC gets added. Thereafter, the string or the id specified in the second parameter is added. And then, the dtd in the third parameter is specified. Therefore, it is either the PUBLIC identifier or the SYSTEM identifier, which would be present. The XML program or the processor scanning the XML file, uses the PUBLIC identifier to retrieve the content for the entities that use the URI. If it fails, it falls back upon to the SYSTEM literal.

Page 174: C#   classes

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument(false);a.Flush();a.Close();}}

b.xml<?xml version="1.0" standalone="no"?>

The WriteStartDocument can take a boolean parameter that adds an attribute which could either be 'standalone = yes' or 'standalone=no', depending upon the value specified. This attribute determines whether the DTD is in an external file or it is internal to the XML file. If the standalone has a value of 'yes', it is suggestive of the fact that there is no external DTD, and therefore, all the grammatical rules have to be placed within the XML file itself.

a.csusing System;using System.Xml;public class zzz{public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);System.Console.WriteLine(a.WriteState);a.WriteStartDocument();System.Console.WriteLine(a.WriteState);a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);System.Console.WriteLine(a.WriteState);a.WriteStartElement("vijay");System.Console.WriteLine(a.WriteState);a.WriteAttributeString ("wife","sonal");System.Console.WriteLine(a.WriteState);a.WriteStartAttribute("hi", "mukhi", "xxx:yyy");System.Console.WriteLine(a.WriteState);a.WriteString("1-861003-78");a.WriteElementString("surname", "mukhi");a.Flush();System.Console.WriteLine(a.WriteState);a.Close();System.Console.WriteLine(a.WriteState);}}

OutputStartPrologPrologElementElementAttributeContentClosed

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay wife="sonal" hi:mukhi="1-861003-78" xmlns:hi="xxx:yyy"> <surname>mukhi</surname></vijay>

The XmlTextWriter object can be in any one of six different states. The WriteState property reveals its current state. When an XmlTextWriter Object is created, it is in the Start state, as may be evident from the fact that, no write method has been called so far. After the Close function, the Writer is in the Closed state. When the WriteStartDocument and WriteDocType functions are called, they reach the Prolog state, because the prolog is being written. The WriteStartElement function actually starts writing to the XML file, thereby, morphing to the Element state. The element start tag 'vijay' begins the XML file. The next function WriteAttributeString does not change the state, since the element in

Page 175: C#   classes

focus still is 'vijay'. The WriteStartAttribute function needs the WriteString to complete the attribute. Thus, after the WriteStartAttribute function executes, the Text Writer assumes the Attribute mode. The surname attribute becomes the content in the XML file. Hence, the state changes to Content mode. This goes on to prove that the TextWriter can possibly be in any one of the above six states, depending upon the entities written to the file. While the TextWrtier is in the Attribute state, it cannot switch to an element state to write an element. Therefore, it throws an exception.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.Namespaces = false;a.WriteStartDocument();a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString("jjj", "bk", "kkk", "sonal:wife");a.Flush();a.Close();}}

OutputUnhandled Exception: System.ArgumentException: Cannot set the namespace if Namespaces is 'false'.at System.Xml.XmlTextWriter.WriteStartAttribute(String prefix, String localName, String ns)at System.Xml.XmlWriter.WriteAttributeString(String prefix, String localName, String ns, String value) at zzz.Main()

The TextWriter class has a Namespaces property that is read-write, and it has a default value of true. The Namespace property is turned off, by setting this property to false. The above runtime exception is thrown because, we have attempted to introduce a namespace jjj, in the WriteAttributeString function.

a.csusing System;using System.Xml;public class zzz {public static void Main() {XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.QuoteChar = '\'';a.Formatting = Formatting.Indented;a.Indentation = 3;a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteAttributeString("jjj", "bk");a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay jjj='bk' />

Various facets of XML can be modified. By using the property QuoteChar, we can modify the default quoting character, from double inverted commas to single inverted commas. Since a single quote cannot be enclosed within a set of single quotes, we use the backslash to escape it. All attributes can now be placed in single quotes instead of double quotes.

a.csusing System;using System.Xml;public class zzz{public static void Main(){XmlTextWriter a = new XmlTextWriter ("b.xml", null);a.WriteStartDocument();a.Formatting = Formatting.Indented;a.Indentation = 3;

Page 176: C#   classes

a.WriteDocType("vijay", null, null ,null);a.WriteStartElement("vijay");a.WriteStartAttribute("hi", "mukhi", "xxx:yyy");a.WriteString("1-861003-78");a.WriteEndAttribute(); a.WriteEndElement();a.WriteEndDocument();a.Flush();a.Close();}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay hi:mukhi="1-861003-78" xmlns:hi="xxx:yyy" />

Good programming style necessitates every 'open' to have a corresponding 'close'. Thus, the Begin functions for an Element, Attribute and Document have corresponding Close functions too. However, if we do not End them, they close by default and no major calamity befalls them. We are using them in the above program as an abandon caution. The WriteEndDocument function puts the Text Writer in the Start mode. Reading an XML file

b.xml<?xml version="1.0" standalone="yes"?><!DOCTYPE vijay SYSTEM "a.dtd" [<!ENTITY baby "No">]><vijay aa="no"><!--comment 2--><?sonal mukhi=no?>Hi&baby;<![CDATA[,mukhi>]]><aa>bb</aa></vijay>

> copy con a.dtdEnter^Z

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main() {XmlTextReader r;r = new XmlTextReader("b.xml");while (r.Read()){Console.Write("{0} D={1} L={2} P={3} ", r.NodeType, r.Depth, r.LineNumber, r.LinePosition );Console.Write(" name={0} value={1} AC={2}",r.Name,r.Value,r.AttributeCount);Console.WriteLine();}}}

OutputXmlDeclaration D=0 L=1 P=3 name=xml value=version="1.0" standalone="yes" AC=2Whitespace D=0 L=1 P=39 name= value= AC=0DocumentType D=0 L=2 P=11 name=vijay value=<!ENTITY baby "No"> AC=1Whitespace D=0 L=2 P=54 name= value= AC=0Element D=0 L=3 P=2 name=vijay value= AC=1Whitespace D=1 L=3 P=16 name= value= AC=0Comment D=1 L=4 P=5 name= value=comment 2 AC=0ProcessingInstruction D=1 L=4 P=19 name=sonal value=mukhi=no AC=0Text D=1 L=4 P=36 name= value=Hi AC=0EntityReference D=1 L=5 P=4 name=baby value= AC=0Whitespace D=1 L=5 P=9 name= value= AC=0CDATA D=1 L=6 P=10 name= value=,mukhi> AC=0Element D=1 L=6 P=21 name=aa value= AC=0Text D=2 L=6 P=24 name= value=bb AC=0EndElement D=1 L=6 P=28 name=aa value= AC=0Whitespace D=1 L=6 P=31 name= value= AC=0EndElement D=0 L=7 P=3 name=vijay value= AC=0

Page 177: C#   classes

In this program, we read an XML file and display all the nodes contained therein. To avoid any errors from being displayed, you should create an empty file by the name of a.dtd. We have a class called XmlTextReader that accepts a filename as a parameter. We pass the filename b.xml to it. This file contains most of the entities present in an XML file. The Read function in this class picks up a single node or XML entity at a time. It returns true, if there are more nodes to be read, or else, it returns false. Thus, when there are no more nodes to be read from the file, the while loop ends. The Read function scans the active node and displays its contents in the loop. The NodeType property displays the name of the nodetype. As an XML file normally starts with a declaration, the NodeType property displays the NodeType as XMLDeclaration, using the ToString function. The Depth property gets incremented by one, every time an element or a tag is encountered. At the Declaration statement, the depth is 0. At the EndElement or at the end of the tag, its value reduces by one. Thus, the Depth property reveals the number of open tags in the file and it can be used for indentation.The Line Number indicates the line on which the statement is positioned, while the LinePosition property displays the position on the line at which the statement begins. The Name property in the class reveals the name of the tag, XML. The output displayed by this property depends upon the active node type. On acute observation, you shall notice that the word XML is not preceded by the symbol <? in the output. The value property relates to the name property, in this case, to XmlDeclaration. It displays the entire gamut of attributes to the node. As there exist two attributes, version and standalone, the property AttributeCount displays a value of 2. If the enter key is pressed after the node declaration, it is interpreted as a Whitespace character. Whitespace characters are separators, which could consist of an enter, space et al. The Position property specifies the character position as 39. The XmlDeclaration has to be the first node in an XML file, and it cannot have any children. The DOCTYPE declaration, which is known as a DocumentType Node, displays the name as vijay, which is the root node. The value is displayed as <!ENTITY baby "No">, which includes everything except the SYSTEM and a.dtd. Thus, in the case of a DocumentType Node, value is the internal DTD. We shall encounter the Whitespace Node very frequently. Hence, we shall not discuss it hereinafter. The Attribute Count will be displayed in the next program. This node can have the Notation and Entity as child nodes. The next node in sequence is our very first element or tag 'vijay', which is the same value that was displayed earlier, with the name property for the DocumentType Node. The Value property for this element shows null, since tags are devoid of Values. Instead, they have Attributes. The attribute Count displays a value of one. At the following Whitespace node, the Depth property gets incremented by one. This is the only way to ascertain whether we are at the root node or not. We now stumble upon a comment, which has no name. The value displayed is the value of the comment. And yet again, the <!-characters are not displayed along with the value. Thereafter, a processing instruction (PI) is encountered. No whitespace is displayed between the comment and the PI, since we have not pressed the Enter key. 'Sonal' becomes the name of the program that runs 'vijay'. The rest turns into the value property having no attributes. TextNode is displayed next because the text 'Hi' is displayed in the XML file. This node too is not assigned any name and the value is depicted as 'Hi'. What follows the text is an Entity Reference. It is assigned the name 'baby' and is devoid of the ampersand sign. Its value is null and it does not have any attributes. The CDATA section is given the name as null. The value is assigned the content of the CDATA, after stripping away the square brackets. The value of the Depth property is incremented by 1. The Text Node follows the element aa. This node does not have any name and it displays the value as 'bb'. In the following program, we explore the various attributes.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlTextReader r;r = new XmlTextReader("b.xml");r.WhitespaceHandling = WhitespaceHandling.None;while (r.Read()){Console.Write("{0} D={1} L={2} P={3}",r.NodeType,r.Depth,r.LineNumber,r.LinePosition);Console.Write(" name={0} value={1} AC={2}",r.Name,r.Value,r.AttributeCount);Console.WriteLine();if (r.HasAttributes){for ( int i =0; i < r.AttributeCount; i++)

Page 178: C#   classes

{r.MoveToAttribute(i);System.Console.WriteLine("Att {0}={1}",r.Name,r[i]);}}}}}

OutputXmlDeclaration D=0 L=1 P=3 name=xml value=version="1.0" standalone="yes" AC=2Att version=1.0Att standalone=yesDocumentType D=0 L=2 P=11 name=vijay value=<!ENTITY baby "No"> AC=1Att SYSTEM=a.dtdElement D=0 L=3 P=2 name=vijay value= AC=1Att aa=noComment D=1 L=4 P=5 name= value=comment 2 AC=0ProcessingInstruction D=1 L=4 P=19 name=sonal value=mukhi=no AC=0Text D=1 L=4 P=36 name= value=Hi AC=0EntityReference D=1 L=5 P=4 name=baby value= AC=0CDATA D=1 L=6 P=10 name= value=,mukhi> AC=0Element D=1 L=6 P=21 name=aa value= AC=0Text D=2 L=6 P=24 name= value=bb AC=0EndElement D=1 L=6 P=28 name=aa value= AC=0EndElement D=0 L=7 P=3 name=vijay value= AC=0

A property called WhiteSpaceHandling is initialized to None, as a result of which, the node Whitespace is not visible in the output. The XmlTextReader has a member HasAttributes, which returns a True value if the node has attributes and False otherwise. Alternatively, we could also have used the property AttributeCount to obtain the number of attributes that the node contains. If the node has attributes, a 'for statement' is used to display all of them. In the loop, we first use the function MoveToAttribute to initially activate the attribute. This is achieved by passing the number as a parameter to the function. Bear in mind that the index starts from Zero and not One. Thereafter, the Name property is used to display the name of the attribute. If the attribute is not activated, the Name property displays the name of the node. This explains the significance of the MoveToAttribute function. As you would recall, the XmlTextReader class has an indexer for the attributes, and like all indexers, it is zero based, i.e. r[0] accesses the value of the first attribute. This is how we display the details of all attributes of the node. For the node DOCTYPE, the SYSTEM becomes the name of the attribute and the value becomes the name of the DTD file. For an element, the attributes are specified in name-value pairs.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static XmlTextReader r;public static void Main(){r = new XmlTextReader("b.xml");int declaration=0, pi=0, doc=0, comment=0, element=0, attribute=0, text=0, whitespace=0,cdata=0,endelement=0,entityr=0,entitye=0,entity=0,swhitespace=0,notation=0;while (r.Read()){Console.Write("{0} D={1} L={2} P={3}",r.NodeType,r.Depth,r.LineNumber,r.LinePosition);Console.Write(" name={0} value={1} AC={2}",r.Name,r.Value,r.AttributeCount);Console.WriteLine();if (r.HasAttributes){for ( int i =0; i < r.AttributeCount; i++){r.MoveToAttribute(i);System.Console.WriteLine("Att {0}={1}",r.Name,r[i]);}}

Page 179: C#   classes

switch (r.NodeType){case XmlNodeType.XmlDeclaration:declaration++;break;case XmlNodeType.ProcessingInstruction:pi++;break;case XmlNodeType.DocumentType:doc++;break;case XmlNodeType.Comment:comment++;break;case XmlNodeType.Element:element++;if (r.HasAttributes)attribute += r.AttributeCount;break;case XmlNodeType.Text:text++;break;case XmlNodeType.CDATA:cdata++;break;case XmlNodeType.EndElement:endelement++;break;case XmlNodeType.EntityReference:entityr++;break;case XmlNodeType.EndEntity:entitye++;break;case XmlNodeType.Notation:notation++;break;case XmlNodeType.Entity:entity++;break;case XmlNodeType.SignificantWhitespace:swhitespace++;break;case XmlNodeType.Whitespace:whitespace++;break;}}Console.WriteLine ();Console.WriteLine("XmlDeclaration: {0}",declaration);Console.WriteLine("ProcessingInstruction: {0}",pi);Console.WriteLine("DocumentType: {0}",doc);Console.WriteLine("Comment: {0}",comment);Console.WriteLine("Element: {0}",element);Console.WriteLine("Attribute: {0}",attribute);Console.WriteLine("Text: {0}",text);Console.WriteLine("Cdata: {0}",cdata);Console.WriteLine("EndElement: {0}",endelement);Console.WriteLine("Entity Reference: {0}",entityr);Console.WriteLine("End Entity: {0}",entitye);Console.WriteLine("Entity: {0}",entity);Console.WriteLine("Whitespace: {0}",whitespace);Console.WriteLine("Notation: {0}",notation);Console.WriteLine("Significant Whitespace: {0}",swhitespace);}}

OutputXmlDeclaration D=0 L=1 P=3 name=xml value=version="1.0" standalone="yes" AC=2Att version=1.0Att standalone=yesWhitespace D=0 L=1 P=39 name= value= AC=0

Page 180: C#   classes

DocumentType D=0 L=2 P=11 name=vijay value=<!ENTITY baby "No"> AC=1Att SYSTEM=a.dtdWhitespace D=0 L=2 P=54 name= value= AC=0Element D=0 L=3 P=2 name=vijay value= AC=1Att aa=noWhitespace D=1 L=3 P=16 name= value= AC=0Comment D=1 L=4 P=5 name= value=comment 2 AC=0ProcessingInstruction D=1 L=4 P=19 name=sonal value=mukhi=no AC=0Text D=1 L=4 P=36 name= value=Hi AC=0EntityReference D=1 L=5 P=4 name=baby value= AC=0Whitespace D=1 L=5 P=9 name= value= AC=0CDATA D=1 L=6 P=10 name= value=,mukhi> AC=0Element D=1 L=6 P=21 name=aa value= AC=0Text D=2 L=6 P=24 name= value=bb AC=0EndElement D=1 L=6 P=28 name=aa value= AC=0Whitespace D=1 L=6 P=31 name= value= AC=0EndElement D=0 L=7 P=3 name=vijay value= AC=0Whitespace D=0 L=7 P=9 name= value= AC=0 XmlDeclaration: 0ProcessingInstruction: 1DocumentType: 0Comment: 1Element: 1Attribute: 0Text: 2Cdata: 1EndElement: 2Entity Reference: 1End Entity: 0Entity: 0Whitespace: 6Notation: 0Significant Whitespace: 0

The above program is a continuation from where we left off in the previous program. The initial portion of the code is identical. A colossal case statement is introduced in the program to check the NodeType. For each Node Type, there is a corresponding variable, whose value is incremented by 1 whenever the Node Type matches. Then, the values contained in these variables are displayed. For inexplicable reasons, the NodeType property does not return the following node types - Document, DocumentFragment, Entity, EndEntity, or Notation.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlTextReader r = new XmlTextReader("b.xml");r.WhitespaceHandling = WhitespaceHandling.None;while (r.Read()){if (r.HasValue)Console.WriteLine("{0} {1}={2}", r.NodeType, r.Name, r.Value);elseConsole.WriteLine("{0} {1}", r.NodeType, r.Name);} }}

OutputXmlDeclaration xml=version="1.0" standalone="yes"DocumentType vijay=<!ENTITY baby "No">Element vijayComment =comment 2

Page 181: C#   classes

ProcessingInstruction sonal=mukhi=noText =HiEntityReference babyCDATA =,mukhi>Element aaText =bbEndElement aaEndElement vijay

The HasValue property simply identifies whether a Node can contain a value or not. There are nine nodes that can possess values. These nodes are Attribute, CDATA, Comment, DocumentType, ProcessingInstruction, Significant Whitespace, Whitespace, Text and XmlDeclaration. All the above nodes must have a value, but they need not necessarily have a name.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlTextReader r = new XmlTextReader("b.xml");r.MoveToContent();string s = r["mukhi"];Console.WriteLine(s);s = r.GetAttribute("sonal");Console.WriteLine(s);s = r[2];Console.WriteLine(s);}}

b.xml<vijay mukhi="no" sonal="yes" aaa="bad" />

Outputnoyesbad

The MoveToContent function moves to the first element in the XML file. In this program, we display the attributes using different methods. In the first approach, the indexer is passed a string, which is the name of the attribute 'mukhi'. It receives 'no' as the return value. In the second approach, the indexer is passed the integer value 2 as a parameter, to access the value of the third attribute, which is 'bad'. Alternatively, the WriteAttribue function could have been given the string 'sonal' as a parameter, to return the value of the attribute as 'yes'. Thus, there are multiple means to achieving the same objective.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main() {XmlTextReader r = new XmlTextReader("b.xml");r.MoveToContent();string s ;s = r.GetAttribute("aa:bb");Console.WriteLine(s);s = r.GetAttribute("bb");Console.WriteLine(s);s = r.GetAttribute("bb","sonal:mukhi");Console.WriteLine(s);s = r.GetAttribute("bb","sonal:mukhi");Console.WriteLine(s);s = r.GetAttribute("bb","aa");Console.WriteLine(s);

Page 182: C#   classes

s = r.GetAttribute("xmlns:aa");Console.WriteLine(s);}} b.xml<vijay xmlns:aa="sonal:mukhi" aa:bb="no" />

Outputno nono sonal:mukhi

The MoveToContent function is used in this program, instead of the Read function. In the file b.xml, we have an attribute bb in the namespace aa. It is initialized to a value of 'no'. The namespace aa has a URI, sonal:mukhi, because of the xmlns declaration. Thus, the full name of the attribute becomes aa:bb i.e. prefix, followed by the colon, followed by the actual name. As a result, specifying aa:bb results in the display of 'no', but only specifying bb as a parameter to GetAttribute results in a null value. The full name of an attribute includes the name of the namespace too. So, we can use the second form of the GetAttribute function that has an overload of two parameters, where the second parameter is the name of the URI and not the namespace. Hence, it is acceptable to call the function with the URI sonal:mukhi, but if we use the namespace aa, no output will be produced. The last GetAttribute utilizes the full name xmlns:aa to retrieve the URI for the element. Thus, we can use this variant of the GetAttribute function with the URI instead of the namespace:name.

a.csusing System;using System.IO;using System.Xml;public class zzz {public static void Main() {XmlTextReader r = new XmlTextReader("b.xml");r.WhitespaceHandling=WhitespaceHandling.None;r.MoveToContent();r.MoveToAttribute("cc");Console.WriteLine(r.Name + " " + r.Value);Console.WriteLine(r.ReadAttributeValue());Console.WriteLine(r.Name + " " + r.Value);}} b.xml<vijay aa="hi" bb="bye" cc="no" />

Outputcc noTrue No

In this example, we directly focus on the attribute that we are interested in, i.e. cc. The name and value properties in XMLTextReader display 'cc' and 'no' respectively. As there are numerous attributes of the node remaining to be read, the ReadAttribute function returns True. This function is normally used to read text or entity reference nodes that constitute the value of the attribute. The Name property of the XmlTextReader however becomes null after the function ReadAttributeValue is called.

8 The DTD Validations in XML

Page 183: C#   classes

So far, we have only read an XML file, without catering to special cases, wherein, either an entity has been used, or data has to be validated as per the element. The XmlTextReader class is the most optimum choice for reading an XML file, barring the cases where data has to be validated, or in cases where an entity has to be replaced with a value. For such purposes, the XmlValidatingReader class is more suited. This class is derived from XmlReader, and it conducts three types of validations- DTD, XDR and XSD schema validations. This class is used when the primary task is either to conduct data validations or to resolve general entities or to provide support for default entities.

a.csusing System;using System.IO;using System.Xml;public class zzz {public static void Main(){XmlValidatingReader r = null;XmlParserContext p;p = new XmlParserContext(null, null, "vijay", null, null, "<!ENTITY pr '100'>","","", XmlSpace.None);r = new XmlValidatingReader ("<vijay mukhi='great' price='Rs &pr;'></vijay>", XmlNodeType.Element, p);r.ValidationType = ValidationType.None;r.MoveToContent();while (r.MoveToNextAttribute()){Console.WriteLine("{0} = {1}", r.Name, r.Value);} r.Close();}}

Outputmukhi = greatprice = Rs 100

To create the object p of type XmlParserContext, the constructor with nine parameters of XmlParserContext class is called. The nine parameters are as follows:

• The first parameter refers to the NameTable type. It has a value of null.• The second parameter refers to NamespaceManager type. It also has a value of null.• The third Parameter is the DocType, i.e. the root tag 'vijay'.• The fourth parameter is the pubid for the external DTD file. • The fifth parameter is the sysid for the external DTD file. • The sixth parameter is the internal DTD, where an ENTITY declaration <!ENTITY pr '100'> has been created. This simply states that the word 'pr' is preceded by a '&' and followed by a semi-colon must be replaced with the string '100'. • The seventh parameter in sequence is the location from where the fragment is to be loaded, i.e. the base URI.• The eighth parameter stands for the xml:lang scope.• The ninth parameter stands for the xml:space scope.

The parameters to the constructor of XmlValidatingReader class are similar to those of the XmlTextReader, which we had encountered earlier. This class is derived from the XmlTextReader as well as the IXmlLineInfo interface. There are five different values that a Validationtype can be initialized to:

1. The first is Auto, which validates only when the DTD or schema information is found. 2. The second is DTD, which validates based on the instructions found in the DTD. 3. The third option, which creates an XML 1.0 non-validation parser, validates the default attributes and resolves entities without using the DOCTYPE. Thus, if the root tag is changed from 'vijay' to 'vijay1', no errors will be generated. Placing the ValidationType statement within comments will generate the following exception:

"Unhandled Exception: System.Xml.Schema.XmlSchemaException: The root element name must match the DocType name. An error occurred at (1, 2)."

4. The fourth option is XSD, which validates as per the XSD schemas. 5. The fifth option is XDR, which validates as per the XDR schemas. In our program we have set this property to a value of None.

Once the required properties are set, the MoveToContent function is used to move to the first element, 'vijay'. The next function, MoveToNextAttribute returns a value of True when there are attributes remaining to be read. Otherwise, it returns a value of False. In our case, it is similar to the MoveToFirstElement function.

Page 184: C#   classes

The while loop repeats twice, since there are two attributes. The Name and Value properties for the first attribute are displayed as 'mukhi' and 'great'. This is very similar to what we have observed in the earlier program. The name for the second attribute is displayed as 'price'. However, its value is not the same, because it has an entity &pr;. The XmlValidatingReader replaces the entity pr with the string '100', prior to displaying the value. Therefore, the output is displayed as 'price' and 'Rs. 100'.

a.csusing System;using System.IO;using System.Xml;using System.Xml.Schema;class zzz{public static void Main(){XmlTextReader r = new XmlTextReader("b.xml");XmlValidatingReader v = new XmlValidatingReader(r);v.ValidationType = ValidationType.DTD;v.ValidationEventHandler += new ValidationEventHandler (abc);while(v.Read());}public static void abc(object s, ValidationEventArgs a){Console.WriteLine("Severity:{0}", a.Severity);Console.WriteLine("Message:{0}", a.Message);}} b.xml<?xml version="1.0" ?><!DOCTYPE vijay1 ><vijay></vijay>

OutputSeverity:ErrorMessage:The root element name must match the DocType name. An error occurred at file:///c:/csharp/b.xml(3, 2).Severity:ErrorMessage:The 'vijay' element is not declared. An error occurred at file:///c:/csharp/b.xml(3, 2).

In the above program, to begin with, an object r that looks like XmlTextReader is created, and then, it is passed to the constructor of XmlValidatingReader, while object v is being created. The ValidationType of the object v is modified to DTD. The ValidationEventHandler event is set to the function abc, which gets called whenever an error occurs. Under the aegis of the Read function, the entire XML file is validated, using the while loop, and the function abc is notified whenever an error is chanced upon. In the function abc, the values contained in the properties - Severity and Message, of the ValidationEventArgs parameter 'a', are printed. The Severity property reveals whether it is an error or warning, whereas, the Message property contains the precise text of the error or warning. In the above case, an error is generated because the DOCTYPE expects the root element to be 'vijay1', whereas, it has been specified as 'vijay'. When no error message is displayed, it may be inferred that no errors have been found. The DTD Using the above C# program, we shall now create our own DTD file. Therefore, we shall modify only the b.xml and b.dtd files.

b.xml<?xml version="1.0" ?><!DOCTYPE vijay SYSTEM "b.dtd" ><vijay /> b.dtd<!ELEMENT vijay >

A DTD is generally very protracted. So, an internal DTD is rarely used. If it is used, its contents have to be placed within [] brackets. To use an external DTD, we use the words SYSTEM followed by the name of the DTD file, which is b.dtd, in this case. In b.dtd, an element 'vijay' is created by inserting the reserved characters '<!', followed by ELEMENT, and finally by the element name 'vijay'. When we run the C# program 'a', the following error is generated:

Output

Page 185: C#   classes

Unhandled Exception: System.Xml.XmlException: This is an invalid content model. Line 1, position 17. An error in the DTD file has resulted in the generation of an un-handled exception. The error occurred due to an incomplete ELEMENT statement.

b.dtd<!ELEMENT vijay EMPTY>

The addition of the word EMPTY salvages the situation. By specifying the word EMPTY, it is amply clear that the element named 'vijay' is an empty element.

b.xml<?xml version="1.0" ?><!DOCTYPE vijay SYSTEM "b.dtd" ><vijay></vijay>

OutputSeverity:ErrorMessage:Element 'vijay' has invalid child element '#PCDATA'. An error occurred at file:///c:/csharp/b.xml(3, 8).

The DTD file states, with absolute clarity, that the ELEMENT 'vijay' is EMPTY. However, an open tag <vijay> and a close tag </vijay>have been added to the XML file. Therefore, an error message is generated, which, as usual, is unintelligible. Instead of using tags such as 'vijay', let us consider a DTD that has been implemented in real life. This one is used for the WML, or the Wireless Markup Language. The rules or syntax of WML are available as a DTD. In our book titled 'WML and WMLScript', we have endeavoured to elucidate the concept of a DTD. You are at liberty to refer to the book. However, we must caution you that, the approach and the explanation used here is entirely at variance with the one used in the earlier book.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml></wml> b.dtd<!ELEMENT wml EMPTY>

OutputSeverity:ErrorMessage:Element 'wml' has invalid child element '#PCDATA'. An error occurred at file:///C:/csharp/b.xml(3, 6).

The word 'vijay' has merely been replaced by the word 'wml'. The error generated is akin to the earlier one. At this juncture, we introduce a 'card' into the DTD file.

b.dtd<!ELEMENT wml (card)>

OutputSeverity:ErrorMessage:Element 'wml' has incomplete content. Expected 'card'. An error occurred at file:///c:/csharp/b.xml(4, 3).

Every WML document must commence with the root tag 'wml'. In the DTD file, we have placed the word 'card' within round brackets, along with wml. This signifies that the wml tag must contain a tag or an element called 'card'. Since there is no card in the XML file, an error is reported, stating that a card is expected, and on account of its unavailability, the wml element is incomplete.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card /></wml>

OutputSeverity:ErrorMessage:The 'card' element is not declared. An error occurred at file:///c:/csharp/b.xml(4, 2)

We add the card tag as a single tag to our XML file, in an endeavour to eliminate the error. But, as we have not specified 'card' as a valid element in the DTD file, yet another error message is displayed. Unless 'card' appears as an ELEMENT in the DTD file, it is not possible to use it in the XML file. Therefore, we now include 'card' as an EMPTY element in b.dtd

Page 186: C#   classes

b.dtd<!ELEMENT wml (card)><!ELEMENT card EMPTY>

Now, all the errors just vanish. In the DTD file, we had affirmed that the element 'card' shall be empty i.e. it will not have any content. The XML file depicted below displays an error, because the 'card' tag is not a single tag any longer.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card></card></wml>

OutputSeverity:ErrorMessage:Element 'card' has invalid child element '#PCDATA'. An error occurred at file:///C:/csharp/b.xml(4, 7).

The error message displayed here is very similar to the one seen with the wml tag.

The element 'wml' has an invalid child element '#PCDATA'A slight modification to the XML file is desirable, before we endeavour to eliminate the error.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card>hi</card></wml>

OutputSeverity:ErrorMessage:Element 'card' has invalid child element 'Text'. An error occurred at file:///c:/csharp/b.xml(4, 7).

Inserting the word 'hi' between the card tags results in a slightly altered error messages. In place of PCDATA, we get to see Text. Resorting to the following modifications to the DTD file, both the error messages can be eliminated.

b.dtd<!ELEMENT wml (card)><!ELEMENT card (#PCDATA)>

To eradicate the errors, the EMPTY word is replaced with #PCDATA, enclosed within round brackets. The word PCDATA is an acronym for Parseable Character Data. In plain English, it represents text that can be entered from the keyboard. Thus, we are at liberty to write as many lines of text as we want, within the card tag. Even if the word 'hi' is removed from within the tags, no error is generated. Our DTD expects a root tag or starting tag of wml. Only a card tag can be inserted amidst within this tag, which is capable of containing limitless content. Insertion of anything else in this tag is a sure recipe for disaster.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card></card><card></card></wml>

OutputSeverity:ErrorMessage:Element 'wml' has invalid content. Expected ''. An error occurred at file:///c:/csharp/b.xml(6, 2).

The above error has occurred because, the DTD clearly specifies that the root tag wml must have one, and only one, occurrence of the tag called 'card' within it. Here, we have created two tags, thereby, causing the error.

b.dtd<!ELEMENT wml (card)*><!ELEMENT card (#PCDATA)>

Page 187: C#   classes

The * symbol, placed after the round brackets, is indicative of the fact that, it can be replaced with zero to infinite values. Thus, the XML file can now either have zero or countless card elements. If you do not give credence to this statement of ours, you may either delete all the card elements from the XML file, or add numerous cards. Either way, no error will be generated.

b.dtd<!ELEMENT wml (card)+><!ELEMENT card (#PCDATA)>

Replacing the symbol * with a + transforms the meaning from 'zero to infinity' to 'one to infinity'. The only difference between the * symbol and the + symbol is that, the + sign mandates at least one occurrence of the element whereas, the * signs makes it optional. Thus, in the aboveXMLfile, at least a single card element is required.

b.dtd<!ELEMENT wml (card)?><!ELEMENT card (#PCDATA)>

The last of the special characters is the symbol ? that specifies the number of elements to be from 'zero or one'. Thus in the XML file, we may have either one card element or none at all. The presence of two or more cards will generate an error. You should try out various possible combinations for each of the symbols *, + and?.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card><p> hi </p></card></wml>

b.dtd<!ELEMENT wml (card)*><!ELEMENT card (p)><!ELEMENT p (#PCDATA)>

No error is generated because, in the DTD file, we have now stated that, the card element can have a tag p, which can contain any text. We have, however, done away with the provision of placing any text within the card tag. Add in a new modification to the file.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card><p> <b/> </p></card></wml>

b.dtd<!ELEMENT wml (card)*><!ELEMENT card (p)><!ELEMENT p (br | b)><!ELEMENT br EMPTY><!ELEMENT b EMPTY>

The DTD appears extensively complicated. The p tag is now competent of containing only two tags, br and b. Text is not allowed any more. The | sign signifies the OR condition, which implies that either tag b or tag br is allowed. The two aforesaid tags are defined as EMPTY tags. To summarise, our DTD states that the p tag can contain a single tag of either b or br.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card><p> <b/> <br/></p></card></wml>

Page 188: C#   classes

OutputSeverity:ErrorMessage:Element 'p' has invalid content. Expected ''. An error occurred at file:///c:/csharp/b.xml(5, 11).

All is not well, because we are allowed to place either a 'b' or a 'br' at a time, but not both together. To remedy the situation, we place a * symbol after the p tag.

b.dtd<!ELEMENT wml (card)*><!ELEMENT card (p)*><!ELEMENT p (br | b)*><!ELEMENT br EMPTY><!ELEMENT b EMPTY>

The above DTD provides us the flexibility of having multiple p tags within n number of cards. These, in turn, may have as many b or br tags as desired. By replacing the b tag with #PCDATA, a p tag is in a position to accommodate multiple br tags, as well as an indefinite amount of text.

<!ELEMENT p (br | #PCDATA)*>

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card /><head /></wml> b.dtd<!ELEMENT wml (card,head)><!ELEMENT card EMPTY><!ELEMENT head EMPTY>

The above DTD file permits the wml tag to contain a card tag, which is then to be strictly followed by a head tag. The comma signifies that one tag is to be followed by the other. If we refrain from using the head tag in the XML file, the following error message will be generated:

OutputSeverity:ErrorMessage:Element 'wml' has incomplete content. Expected 'head'. An error occurred at file:///C:/csharp/b.xml(5, 3).

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><head /><card /></wml>OutputSeverity:ErrorMessage:Element 'wml' has invalid content. Expected 'card'. An error occurred at file:///c:/csharp/b.xml(4, 2).

If the order of the tags is interchanged, an error is thrown. The card tag must be followed by the head tag. Besides, there is a restriction imposed that there can be only one insertion of each tag. If there are multiple insertions, it will result in an error.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card /><card /><head /></wml> b.dtd<!ELEMENT wml (card+,head?)><!ELEMENT card EMPTY><!ELEMENT head EMPTY>

Page 189: C#   classes

When the plus sign is inserted after the card, it allows the use of more that one card tag in the file. The ? sign denotes 'zero or one' insertions of the head tag. Thus, we can have more than one card tag and have either a single head tag or none at all. If the head tag is present, it must be placed after the card tag, since the order of the tags is sacrosanct.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card /><head /><card /></wml>

OutputSeverity:ErrorMessage:Element 'wml' has invalid content. Expected ''. An error occurred at file:///c:/csharp/b.xml(6, 2).

The Draconian restrictions imposed by the DTD file prohibit us from altering the sequence of the above tags. The card tag has to come first, followed by the head tag. We cannot interchange a head tag with a card tag. So, the only solution to this problem is to abide by the stipulated sequence.

b.dtd<!ELEMENT wml (card+,head?,template*)*><!ELEMENT card EMPTY><!ELEMENT head EMPTY><!ELEMENT template EMPTY>

In the DTD file, we have added a * symbol to the entire set of tags, which make up the wml element. The set consists of the following individual elements in a sequential order:

• More than one card tags.• Zero or one head tag. • Zero to many template tags.

This set can constitute of numerous permutations and combinations of the above conditions, in the specified order. Thus, the card and head can appear together, or the card can appear by itself without the head tag, or the template tag may not be present at all, and so on. Every occurrence, however, needs to begin with a card tag.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="hi"/></wml>

b.dtd<!ELEMENT wml (card)><!ELEMENT card EMPTY><!ATTLIST card aa CDATA #IMPLIED>

In the above example, the card tag has an attribute called aa initialized to 'hi'. To implement an attribute, we include the word ATTLIST, which is a short form for 'a list of attributes', in the DTD file. This is followed by the name of the tag that the attribute is associated with. Then, the actual name of the attribute aa is specified, followed by the datatype it will hold, which is character data, in our case. The last parameter, #IMPLIED permits the attribute aa to be optional. Therefore, even if you remove it, no error will be generated.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card /></wml> b.dtd<!ELEMENT wml (card)><!ELEMENT card EMPTY><!ATTLIST card aa CDATA #IMPLIED bb CDATA #REQUIRED>

OutputSeverity:Error

Page 190: C#   classes

Message:The required attribute 'bb' is missing. An error occurred at file:///c:/csharp/b.xml(4, 2). The error message clearly mentions that the attribute bb is missing. The #REQUIRED demands the presence of attribute bb, along with the card, whenever the card tag is used. Further, the attributes are to be placed one after the other. However, the order of placement is not significant.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card bb="no"/></wml>

No errors are generated since the attribute bb, which is mandatory, has been specified. You can avoid aa, since it is implied.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="no"/></wml> b.dtd<!ELEMENT wml (card)><!ELEMENT card EMPTY><!ATTLIST card aa (hi | bye ) "bye">

OutputSeverity:ErrorMessage:'no' is not in the enumeration list. An error occurred at file:///c:/csharp/b.xml(4, 7).

The values assigned to attributes can be restricted to specific values. This can be achieved by specifying the values along with ATTLIST in the DTD file and using the OR sign (|) as the separator. The attribute aa can only be assigned the value of either 'hi' or 'bye'. Specifying any other value would result in an error. If the attribute is not initialized, it assumes the default value of 'bye'.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="hi"/></wml>

The error disappears because the attribute has been assigned a value of 'hi'.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="hi"/></wml> b.dtd<!ELEMENT wml (card)*><!ELEMENT card EMPTY><!ATTLIST card aa ID #IMPLIED>

We have created an attribute aa, with a data type of ID. This does not result in any error.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="hi"/><card aa="hi"/></wml>

OutputSeverity:ErrorMessage:'hi' is already used as an ID. An error occurred at file:///c:/csharp/b.xml(5, 7).

Page 191: C#   classes

The card tag can be used multiple times, due to the presence of the * sign in the DTD file. By associating the type of ID to the attribute aa, it is guaranteed that the same value of 'hi' is not assigned to the attribute. The error message conveys that 'hi' has already been assigned as an ID to the attribute aa, and hence, it cannot be used again.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card aa="hi"/><card aa="hi1"/></wml>

If we assign a different value to the attribute, the error is dispensed with. Thus, a data type of ID guarantees that the attribute shall never have a duplicate value.

b.xml<?xml version="1.0" ?><!DOCTYPE wml SYSTEM "b.dtd" ><wml><card>Hi &sonal;</card></wml> b.dtd<!ELEMENT wml (card)*><!ELEMENT card (#PCDATA)*><!ENTITY sonal "hi" >

Entities have been touched upon earlier. Here, the word 'sonal' will be replaced with 'hi'. This is called an Entity Reference. The DTD file requires an ENTITY word with the variable 'sonal', and the value 'hi'.

9 XML Data Document

a.csusing System;using System.IO;using System.Xml;public class zzz {public static void Main() {XmlDocument d = new XmlDocument();XmlTextReader r = new XmlTextReader("books.xml");r.WhitespaceHandling = WhitespaceHandling.None;//r.MoveToContent();d.Load(r);d.Save(Console.Out);}} books.xml<?xml version='1.0'?><!-- This file represents a fragment of a book store inventory database --><bookstore><book genre="autobiography" publicationdate="1981" ISBN="1-861003-11-0"><title>The Autobiography of Benjamin Franklin</title><author><first-name>Benjamin</first-name><last-name>Franklin</last-name></author><price>8.99</price></book><book genre="novel" publicationdate="1967" ISBN="0-201-63361-2"><title>The Confidence Man</title><author><first-name>Herman</first-name>

Page 192: C#   classes

<last-name>Melville</last-name></author><price>11.99</price></book><book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6"><title>The Gorgias</title><author><name>Plato</name></author><price>9.99</price></book></bookstore>

Output<?xml version="1.0"?><!-- This file represents a fragment of a book store inventory database --><bookstore>..

The XmlDocument class represents an XML document. The W3C has two standards, one called the Document Object Model DOM Level 1, and the other called the Core DOM Level 2. The XmlDocument class implements these standards. This class is derived from an abstract class called XmlNode. The XML Document is loaded into memory as a tree structure, thereby facilitating the navigation within and the editing of the document. The representation of the document in memory is known as a DOM. The XML file books.xml represents a bookstore , which stores a large number of books. This file shall not be displayed in future since it is pretty lengthy. This file is utilised by most of the XML samples supplied by Microsoft. The tag 'books' represents a book, and has attributes such as, type of book, genre, the year of publication or publication date etc; and finally, a unique number, ISBN, assigned to the book. Each book has a title as well as an author. The attribute author contains a few tags that disclose details such as, the first name and the last name of the author. The price of the book is the last element. It is not imperative for these elements of a book to be present in a specific order, i.e. price can come before the title, and so on. The object r of type XmlTextReader class represents the XML file books.xml. As always, the WhiteSpaceHandling is set to None. D is an object that looks like XmlDocument. The Load function of this class can handle four overloads. One of the overloads that the function can take is an XmlReader. As we are supplying an XmlTextReader to this function, it eventually scales down to an XmlReader. The Load function is responsible for loading the XML file into the XmlDocument object. If the property PreserveWhitespace is set to True, then and only then, are the Whitespace nodes created. In this case, since we have set Whitespaces to None in the XML file, no white space nodes can ever be created, regardless of property PreserveWhitespace being set to True. The Load function cannot change the Whitespace Handling present in the reader. The Load method does not perform any validations. In case validations are imperative, the XmlValidatingReader class must be utilised in place of the plain Jane XmlTextReader class. The same argument holds good when entities have to be resolved. The Save function writes the XML document associated with the XmlDocument, either to disk or to the console. We have displayed only a section of the document, since the entire books.xml shall occupy space needlessly. We acquire the entire file because the ReadState is set to Initial. This is so because the state has not been changed yet. Let us now position the reader and see what happens. We remove the comment marks associated with MoveToContent, thereby, making it available. As a consequence, both, the XmlDeclaration and the content are skipped, and the reader is positioned at the first node 'Bookstore'. The output is as displayed below:

Output<bookstore> <book genre="autobiography" publicationdate="1981" ISBN="1-861003-11-0">…</bookstore>

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();XmlTextReader r = new XmlTextReader("books.xml");r.WhitespaceHandling = WhitespaceHandling.None;r.MoveToContent();

Page 193: C#   classes

r.Read();r.Skip();r.Skip();d.Load(r);d.Save(Console.Out);}}

Output<book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6"> <title>The Gorgias</title> <author> <name>Plato</name> </author> <price>9.99</price></book>

The Read function takes the liberty to position itself at the book tag, the first Skip function skips over the first book, and the second skip function skips over the second book. The Load function of the XmlDocument now loads the node from the reader's current position, i.e. from the third book onwards. Thus, the XmlDocument contains the third book, which is about Philosophy, and not the other two books. The skip function skips over nodes that are at the same level.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<aa bb='hi'> bad </aa>");d.Save("d.xml");}} d.xml<aa bb="hi"> bad </aa>

There are different ways of associating XML with an XmlDocument object. The XmlDocument class has a function called LoadXml that converts a string representing XML into an XmlDocument object. The Save function writes the contents to a file on disk named d.xml.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();d.CreateElement("vijay");d.Save("d.xml");}}

OutputUnhandled Exception: System.Xml.XmlException: This an invalid XML document. The document does not have a root element.

The CreateElement function, as the name suggests, creates an element with the name passed as a parameter. We encounter an impediment when we attempt to write it to disk. An exception is thrown, since we have not added the element to the XmlDocument. Had we used the Save overload that writes to Console.Out, no exceptions would have been thrown, but neither would any output have been displayed on the screen.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main() {

Page 194: C#   classes

XmlDocument d = new XmlDocument();XmlElement e;e = d.CreateElement("vijay");d.AppendChild(e);d.Save("d.xml");}} d.xml<vijay />

The CreateElement function returns an XmlElement object, which is stored in e. This object is then passed on to the AppendChild function, which creates a child node. Thus, when we write the XmlDocument to disk, d.xml will display a single tag of 'vijay'. We shall now examine a series of programs, which shall create each and every type of node and shall write them all to disk.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();XmlElement e;e = d.CreateElement("vijay");XmlAttribute a = d.CreateAttribute("a1");e.SetAttributeNode(a);e.SetAttribute("a1", "howdy");d.AppendChild(e);d.Save("d.xml");}} d.xml<vijay a1="howdy" />

CreateElement creates an element called 'vijay'. Thereafter, a function called CreateAttribute creates an attribute called a1. This function returns an XmlAttribute object that is associated with the element e, using the function SetAttributeNode, off the XmlElement class. If we comment the line e.SetAttribute("a1", "howdy"); we shall see the file d.xml containing the following:

d.xml<vijay a1="" />

The attribute gets created, but it is devoid of any value at this stage. It is the SetAttribute function from the XmlElement class that will assign the value to the attribute. Hence, it accepts two parameters. The first parameter is a1, which is the name of the attribute, and the second parameter 'howdy' is the value of the attribute.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();XmlElement e;e = d.CreateElement("vijay");d.AppendChild(e);XmlCDataSection c;c = d.CreateCDataSection("sonal mukhi"); d.AppendChild(c); d.Save("d.xml");}}

OutputUnhandled Exception: System.InvalidOperationException: The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type.

Page 195: C#   classes

We first create an element 'vijay' by using the CreateElement function. Thereafter, using the Append Child function, we add the element to the XmlDocument. No errors are generated upto this point. Now, we wish to add a CDATA section to the file. To achieve this, the CreateCDataSection is used with a string parameter 'sonal mukhi', which returns an XmlCDataSection object. Employing the familiar AppendChild function, the section is added to the file. Since an XML document cannot have a CDATA section by itself, the above exception is generated.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();XmlElement e;e = d.CreateElement("vijay");d.AppendChild(e);XmlCDataSection c;c = d.CreateCDataSection("sonal mukhi"); XmlElement r = d.DocumentElement;r.AppendChild(c); d.Save("d.xml");}}

d.xml<vijay><![CDATA[sonal mukhi]]></vijay>

The error now disappears, as the AppendChild function is called from the XmlElement class, and not from the XmlDocument class. The DocumentElement property represents the root or the first element of theXMLdocument. The CDATA section now gets added to the element 'vijay', since the root element is 'vijay'.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main() {XmlDocument d = new XmlDocument();d.LoadXml("<vijay> hi </vijay>");XmlCDataSection c;c = d.CreateCDataSection("sonal mukhi"); XmlElement r = d.DocumentElement;r.AppendChild(c); d.Save("d.xml");}}d.xml<vijay> hi <![CDATA[sonal mukhi]]></vijay>

An alternative approach would be to use the trustworthy LoadXml function, in lieu of CreateElement. Any of the two approaches may be employed to add a root node, thereby, displaying the d.xml file. As we have been harping repeatedly, there are many ways to skin a cat.

a.csusing System.Xml;public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<!DOCTYPE book > <book /> ");XmlDocumentType t;t = d.DocumentType;System.Console.WriteLine(t.OuterXml);System.Console.WriteLine(t.Name);

Page 196: C#   classes

}}

Output<!DOCTYPE book[]>book

This program demonstrates our ability to access a specific node while processing an XML file. The DocumentType property returns an XmlDocumentType t, which represents the singular DOCTYPE node present in an XML file. This object t has a large number of properties that facilitate access to all the details regarding the node. In the program, only two properties are displayed:

• The first property OuterXml represents the entire node as a string. Since no external DTD file is present, by default, the internal DTD, which does not have any contents, is displayed. • The second property Name presents the name of the root node.

a.csusing System;using System.Xml;public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<book> <title>None</title> <author>Me</author> </book>");XmlNode r = d.FirstChild;XmlNode n = r.FirstChild;Console.WriteLine(r);Console.WriteLine(n.OuterXml);Console.WriteLine(r.OuterXml);}}

OutputSystem.Xml.XmlElement<title>None</title><book><title>None</title><author>Me</author></book>

The FirstChild property of the XmlDocument class or XmlNode class retrieves the first child of the current node in the document i.e. XmlElement. The value is stored in both r and n. It can be displayed using the WriteLine function. The OuterXml property contains the tags, including the child nodes. The FirstChild property of r is an XmlNode, whose OuterXml is the first child node or tag title within the tag 'book'. If no child node exists, the value is null.

a.csusing System;using System.Xml;public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<book> <title>None</title> <author>Me</author> </book>");XmlNode r = d.LastChild;XmlNode n = r.LastChild;Console.WriteLine(r);Console.WriteLine(n.OuterXml);Console.WriteLine(r.OuterXml);}}

OutputSystem.Xml.XmlElement<author>Me</author><book><title>None</title><author>Me</author></book>

Comparable to the FirstChild property is the LastChild property, which merely returns the last child in the node. Therefore, as the 'author' tag is the last tag, the LastChild property returns a reference to 'Me'. Since title is the only tag in the file, the use of FirstChild or LastChild achieves the same outcome, in this case.

a.csusing System;using System.Xml;

Page 197: C#   classes

public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<aa><book> hi<title>None</title> </book> </aa>");XmlDocument de = (XmlDocument) d.CloneNode(true);Console.WriteLine(de.ChildNodes.Count);for (int i=0; i<de.ChildNodes.Count; i++)Console.WriteLine(de.ChildNodes[i].OuterXml);Console.WriteLine(de.Name + de.OuterXml);Console.WriteLine("Shallow");XmlDocument sh = (XmlDocument) d.CloneNode(false);Console.WriteLine(sh.ChildNodes.Count);for (int i=0; i<sh.ChildNodes.Count; i++)Console.WriteLine(sh.ChildNodes[i].InnerXml);Console.WriteLine(sh.Name + sh.OuterXml);}}Output1<aa><book> hi<title>None</title></book></aa>#document<aa><book> hi<title>None</title></book></aa>Shallow0#document

We start with an XML fragment that has a root node aa, which has a child node of book, containing another child node called title. The CloneNode function in XmlDocument takes a boolean parameter, where True refers to cloning the nodes within the node aa, which includes the nodes 'book' and 'title'. It behaves akin to a copy constructor for nodes. The cloned node does not have a parent. Therefore, when we exploit the property ParentNode to ascertain the value of the parent node, it returns a value of null. The ParentNode property returns a handle to the Parent node of the node. The return value of the CloneNode function is an XmlNode class. As the XmlDocument derives from XmlNode, the 'cast' operator is used. The XmlNode class has a property called ChildNodes, which returns an XmlNodeList object that refers to all the child nodes. This object is of a Collection data type. The Count property in the collection can be used to render a count of the number of entities present in the clone. The Name property returns the qualified name of #document and the OuterXml property provides the entire element. This function at variance with the LocalName function, which returns the name of an attribute, #cdata-section for the CDATA section, and so on. Thus, with the help of this function, we can identify the different names for different node types. The CloneNode function is called again, but with a value of false. Thus, a shallow node with no ChildNodes is returned. This is confirmed using the count property, which shows a count of zero. This proves that only the node has been cloned and not the content. Using a 'for' statement, we iterate through all the child nodes, and display the OuterXml property using the indexer. The value returned is the content of the node, which includes text such as 'hi'. If you read about the CloneNode function in the documentation, it would apprise you of the impending eventuality, if an attempt is made to clone nodes of different types.

a.csusing System;using System.IO;using System.Xml;public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<!DOCTYPE book [<!ENTITY bb 'vijay'>]> <book> <misc /> </book>"); XmlEntityReference e = d.CreateEntityReference("bb");Console.WriteLine(e.ChildNodes.Count);d.DocumentElement.LastChild.AppendChild(e);Console.WriteLine(e.FirstChild.InnerText + ". " + e.ChildNodes.Count + " " + e.Name);d.Save(Console.Out);XmlEntityReference e1 = d.CreateEntityReference("bbb");Console.WriteLine(e1.ChildNodes.Count);d.DocumentElement.LastChild.AppendChild(e1);Console.WriteLine(e1.FirstChild.InnerText + ". " + e1.ChildNodes.Count + " " + e1.Name);d.Save(Console.Out);}}

Page 198: C#   classes

Output0vijay. 1 bb<!DOCTYPE book[<!ENTITY bb 'vijay'>]><book> <misc>&bb;</misc></book>0. 1 bbb<!DOCTYPE book[<!ENTITY bb 'vijay'>]><book> <misc>&bb;&bbb;</misc></book>

The LoadXml function has a DOCTYPE declaration, and an entity reference called bb having a value of 'vijay'. Now, using the function CreateEntityReference, we create an entity reference called bb. This function returns an XmlEntityReference object, which is a class derived from XmlLinkedNode, which, in turn, is derived from XmlNode. The number of child nodes is zero at this stage, because the node has not yet been expanded. The function AppendChild is then used to append the child to the node, returned by the LastChild property. Since the node has been appended to the document, its parent node is set and the entity reference bb is expanded to 'vijay'. Thus, the file has a child node that contains 'vijay', the entity reference text. The InnerText property of the FirstChild gives the replacement text. The Count of the child nodes is one, since we have added one entity, and the Name property of the Entity Reference is the name bb, which we have created. Finally, the Save function, which prints out the XML fragment, displays the entity reference bb, starting with an ampersand symbol & and ending with a semi-colon, with the LastChild node named misc. We now add another entity reference bbb, but lay it aside, undefined. The rules, which applied earlier to the entity reference, are also relevant now. The only significant difference is that the InnerText property does not have any value when the reference node is expanded. Thus, the child is an empty text node. The entity refs of bb and bbb are placed one after the other.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main() {XmlDocument d = new XmlDocument();d.LoadXml("<book></book>"); XmlNode n=d.CreateNode("element", "vijay", ""); n.InnerText = "hi";XmlElement r = d.DocumentElement;r.AppendChild(n);//d.LastChild.AppendChild(n);//d.AppendChild(n);Console.WriteLine(d.OuterXml);d.Save(Console.Out);}}

Output<book><vijay>hi</vijay></book><book> <vijay>hi</vijay></book>

The XML fragment shows the root tag as 'book' because of the LoadXml function. The CreateNode function creates a new node or element called 'vijay'. This function returns an object of type XmlNode and accepts the following parameters: The first parameter decides on the type of node. Exactly 10 types of nodes can be created with this function: element, attribute, text, cdatasection, entityreference, processinginstruction, comment, document, documenttype and documentfragmnet. This parameter is case sensitive. The second parameter is the name of the new node. If there is a colon in the name, it is parsed into a prefix and a LocalName. The third parameter refers to the namespace URI. The InnerText property is another mechanism for adding content between tags. The tag 'vijay' now embodies the word 'hi'. This node in memory is added to the XML fragment with the help of the AppendChild function, which is called off the XmlElement, returned by the DocumentElement property.

Page 199: C#   classes

Calling AppendChild off the XmlDocument class, throws an exception, since a DocumentElement node already exists within the document. If we uncomment the line d.AppendChild(n), it will result in the following exception:

OutputUnhandled Exception: System.InvalidOperationException: This document already has a DocumentElement node.

If we uncomment the line d.LastChild.AppendChild(n), it would be similar to first gaining a handle to the last child and then adding the node. In this case, whether the node is the first child or the last child, it does not make any difference at all. The documentation for the CreateNode function, offers a table that very distinctly specifies as to which nodes can be placed within other nodes.

a.csusing System;using System.Xml;public class zzz {public static void Main(){XmlDocument d = new XmlDocument();d.Load("b.xml");XmlImplementation i;i = d.Implementation;XmlDocument d1 = i.CreateDocument();d.Save(Console.Out);d1.Save(Console.Out);}}

b.xml<?xml version="1.0"?><!DOCTYPE vijay><vijay />

Output<?xml version="1.0"?><!DOCTYPE vijay[]><vijay />

Every XmlDocument has an XmlImplementation object associated with it, which can be accessed using the Implementation property. XmlDocument objects created from the same XmlImplementation, share the same name table. Thus, it is permissible to compare attribute and element names as objects, instead of strings. We create an empty XmlDocument d by using the constructor as before, and thereafter, use the Load function to associate anXMLfile named b.xml with the XmlDocument. Earlier we had used the Reader object. To create an XmlDocument object that shares the same XmlNameTable, we use the function CreateDocument from the XmlImplementation object. Then, the Save function is used to write out both the XmlDocument objects to the Console. The first XmlDocument object d, displays a replica of the file b.xml, while the second one is empty because we have not associated any XML content with it. Thus, the CreateDocument function creates an empty XML document, but it does not copy any content to it. It ensures that the names are shared, and not duplicated.

a.csusing System;using System.IO;using System.Xml;public class zzz{public static void Main(){XmlTextReader r = new XmlTextReader ("books.xml");r.WhitespaceHandling = WhitespaceHandling.None;XmlDocument d = new XmlDocument();d.Load (r);XmlNode n;n = d.DocumentElement;Console.WriteLine(n.Name + "<" + n.Value + ">");n = n.FirstChild;Console.WriteLine(n.Name + "<" + n.Value + ">");while (n != null){XmlNamedNodeMap m = n.Attributes;foreach (XmlNode a in m)Console.Write(" " + a.Name + "<" + a.Value + "> ");

Page 200: C#   classes

n = n.NextSibling;Console.WriteLine();}}}

Outputbookstore<>book<> genre<autobiography> publicationdate<1981> ISBN<1-861003-11-0> genre<novel> publicationdate<1967> ISBN<0-201-63361-2> genre<philosophy> publicationdate<1991> ISBN<1-861001-57-6>

First, we start by loading the XML file books.xml, and then, positioning the DocumentElement at the root tag bookstore. Then, using the FirstChild property, we make the first child node 'book' as the active node and display it. As we have no clue about the number of child book nodes that are present in the file, we use a while loop that repeats itself until the XmlNode object n returns a null. The Attributes property returns an XmlNamedNodeMap object that represents a collection of nodes. These nodes can be represented by a name or an index. So, using the foreach loop, we fetch one attribute at a time in an XmlNode object and display the Name and the Value contained in it. The <> signs are used as a talisman. Once this is accomplished, we move to the next book at the same level. Tags at the same level are called siblings. The property NextSibling moves to the next book or tag at the same level, thereby, displaying one book tag after another.

a.csusing System;using System.Xml;public class Sample{public static void Main(){XmlTextReader r = new XmlTextReader ("books.xml");r.WhitespaceHandling = WhitespaceHandling.None;XmlDocument d = new XmlDocument();d.Load (r);XmlNode n = d.DocumentElement;Console.WriteLine(n.Name);XmlNode b = n.LastChild;Console.WriteLine(b.Name);Console.WriteLine(b.OuterXml + "\n");b = n.LastChild.PreviousSibling;Console.WriteLine(b.OuterXml); }}

Outputbookstorebook<book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6"><title>The Gorgias</title><author><name>Plato </name></author><price>9.99</price></book>

In the previous example, we first displayed the root node bookstore, followed by the LastChild and not the FirstChild node. Both are expected to return the same name, since the first and last child tags refer to the same node book. The XML associated with this node was displayed using the OuterXml function, which displayed the last book, which deals with Philosophy. You may recollect that the OuterXml function displays the child tags also. In this example, we proceed backwards. Hence, we use the PreviousSibling function to display the second last book. Thus, we can either proceed in the forward direction, like we did in the last example, or in the backward direction, as shown in the current example.

a.csusing System;using System.IO;using System.Xml;public class Sample{public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<book> hi </book>");XmlComment c;c = d.CreateComment("Comment 1");

Page 201: C#   classes

XmlElement r = d.DocumentElement;d.InsertBefore(c, r);d.Save(Console.Out);Console.WriteLine("\n---");XmlComment c1;c1 = d.CreateComment("Comment 2");d.InsertAfter(c1, r);d.Save(Console.Out);}}

Output<!--Comment 1--><book> hi </book>---<!--Comment 1--><book> hi </book><!--Comment 2-->

LoadXml creates a node called book, which encompasses 'hi'. Thereafter, a Comment object is created by calling the CreateComment function. The function merely requires the string to be displayed as a comment. The extra comment characters are placed by the function in the XML file. The next dilemma is with regard to the position of the comment i.e. should the comment be placed before or after the node book. The InsertBefore function inserts the required node. It takes two parameters, i.e. a comment, followed by the node before which the comment is to be inserted. As we want it to be inserted before the book node, we use the handle returned by DocumentElement property, which contains the handle to the book node. The Save function then displays the comment before the book node. The InsertAfter function inserts the comment node after the reference node, which has been passed as the second parameter. Thus, the second comment comes after the book node.

a.csusing System;using System.IO;using System.Xml;public class zzz {public static void Main(){XmlDocument d = new XmlDocument();d.LoadXml("<?xml version='1.0' ?><book> <title>Vijay</title> </book>");XmlNode n = d.DocumentElement;n.RemoveChild(n.FirstChild);d.Save(Console.Out);d.RemoveAll();Console.WriteLine("\n=========");d.Save(Console.Out);}}

Output<?xml version="1.0"?><book></book>=========

The XML fragment that is loaded using the LoadXml function has an XML Declaration, which is a tag book, containing an element of title enclosing 'Vijay'. The DocumentElement property returns a handle to the node book, which is stored in n. Then, the RemoveChild function of node n is called with a single parameter, which is the node title, since the FirstChild returns this value. This function removes the child from the tag.Saving to the Console displays the remaining XML fragment. Thus, we can see the XML declaration and the tag book, but without any content, since the tag title has been removed. The next function that is implemented is, RemoveAll from the XmlDocument class. On displaying the XML file, we witness no output since the RemoveAll function erases everything from the Document.

a.csusing System;using System.IO;using System.Xml;public class zzz {

Page 202: C#   classes

public static void Main(){XmlNode n;XmlDocument d = new XmlDocument();d.LoadXml("<zz xml:space=\"preserve\"><aa>Vijay</aa><bb>Mukhi</bb></zz>"); Console.WriteLine(d.DocumentElement.InnerText);n=d.DocumentElement;XmlSignificantWhitespace s=d.CreateSignificantWhitespace(" ");d.Save(Console.Out);Console.WriteLine();n.InsertAfter(s, n.FirstChild);Console.WriteLine(d.DocumentElement.InnerText);d.Save(Console.Out);}}

OutputVijayMukhi<zz xml:space="preserve"> <aa>Vijay</aa> <bb>Mukhi</bb></zz>Vijay Mukhi<zz xml:space="preserve"> <aa>Vijay</aa> <bb>Mukhi</bb></zz>

The DocumentElement property returns a handle to the zz tag, whereas, the InnerText property in the DocumentElement, refers to the Text present within the tags aa and bb. Thus, we see 'VijayMukhi' displayed without any spaces displayed between the two words. We also create significant whitespace, using the function CreateSignificantWhitespace, after initializing the node n to tag zz. This function accepts only one string parameter, which could be any one of the following four: &#20; &#10; &#13; and &#9. It does not augment theXMLfragment. Finally, we write the XML fragment using the Save function. The attribute space=preserve is visible. This attribute is optional. Using the InsertAfter function, we add significant whitespace after the FirstChild node. Thus, we see significant space between the nodes aa and bb. The InnerText also displays the spaces between the words 'Vijay' and ' Mukhi'.

OutputVijayMukhi<zz xml:space="preserve"> <aa>Vijay</aa> <bb>Mukhi</bb></zz> VijayMukhi<zz xml:space="preserve"> <aa>Vijay</aa><bb>Mukhi</bb></zz>

The above output is obtained on adding the significant space before the first child, using the InsertBefore function, in lieu of the InsertAfter function. The spaces are inserted before the FirstChild aa, i.e. before the text 'Vijay' and not after.

a.csusing System;using System.Collections;using System.Xml;public class zzz{public static void Main(){XmlDocument d = new XmlDocument();d.Load("books.xml");XmlNode n = d.DocumentElement;IEnumerator i = n.GetEnumerator();XmlNode b;while (i.MoveNext()) { b = (XmlNode)i.Current;Console.WriteLine(b.OuterXml);}}}

Output

Page 203: C#   classes

<book genre="autobiography" publicationdate="1981" ISBN="1-861003-11-0"><title>The Autobiography of Benjamin Franklin</title><author><first-name>Benjamin</first-name><last-name> Franklin</last-name></author><price>8.99</price></book><book genre="novel" publicationdate="1967" ISBN="0-201-63361-2"><title>The Confidence Man</title><author><first-name> Herman</first-name><last-name>Melville</last-name></author> <price>11.99</price></book><book genre="philosophy" publicationdate="1991" ISBN="1-861001-57-6"><title>TheGorgias</title><author><name>Plato</name></author><price> 9.99</price></book>

The last example in the series, displays various parts of an XML file in a different manner. Every XML node object has a function called GetEnumerator, which returns an IEnumerator interface object. This interface has a MoveNext function that moves from one node to the next. In our program, we move from one book to another, since the XmlNode is on a tag book. The Current property accesses the current book node, since it happens to be the first one. Then, we use the OuterXml property to display the entire contents of this node. The MoveNext function then activates the next node. If no more nodes exist, it returns False, or else it returns True. In this manner, we can iterate through all the nodes. The foreach instruction has a similar functionality. Whenever we want to navigate through a collection, the IEnumerator interface is used. This is the conventional way of moving sequentially through a list of objects or a collection in C#.

10 XML Documentation XML is used extensively to document program code. Apart from its status of a mere acronym for eXtensible Markup Language, XML has a bounden duty to achieve miracles. However, in this chapter, we would be utilizing XML for the temporal task of documenting our code.

a.csclass zzz{public static void Main() {}}

Run the compiler as

>csc a.cs /doc:a.xml

a.xml<?xml version="1.0"?><doc> <assembly> <name>a</name> </assembly> <members> </members></doc>

This is the smallest possible XML file that can be effectively generated by the compiler. The compiler with the /doc option, followed by a colon and filename, creates the XML file, which documents the C# program. Anything placed within the <> brackets is called a tag. At the expense of sounding tediously repetitive, we once again reiterate that, each and every XML file must necessarily start with a tag beginning with <?xml>. This has to be followed by an attribute called version. Presently, the only permissible value that can be assigned to version is 1.0. There may never be a version 1.1 or 2.0. Subsequent to this, a root tag called 'doc' is specified, which envelopes all other tags within the file. We have a tag called 'assembly' which owns another tag called 'name' that encloses our assembly name 'a'. The name of our program file is 'a'. There are no members to be documented, therefore, no names are noticeable within the member tags. This is the basic structure of our XML file. It is the bare minimum, no frills structure, created by the compiler. Now, let us refurbish and garnish it through our own contribution.

a.cs

Page 204: C#   classes

class zzz{/// <vijay>public static void Main() {}}

We have added an XML tag in the .cs file. The documentation clearly states that an XML tag in a .cs file cannot begin with two slashes, since they are reserved for comments. It should instead have three slashes. The use of the two slashes generates the following warning:

Outputa.cs(4,3): warning CS1570: XML comment on 'zzz.Main()' has badly formed XML -- 'End tag 'member' does not match the start tag 'vijay'.'

From now on, we shall only display the pertinent portions of the XML file, while ignoring the portion that is invariant, such as, the doc and the /doc tags. So in the file created as:

a.xml<?xml version="1.0"?><doc> <assembly> <name>a</name> </assembly> <members> <!-- Badly formed XML comment ignored for member "M:zzz.Main" --> </members></doc>

we will display only

xml fragment: <members> <!-- Badly formed XML comment ignored for member "M:zzz.Main" --> </members>

In the next program, a closing tag of 'vijay' is specified.

a.csclass zzz{/// <vijay>/// Function Main gets called first/// </vijay>public static void Main() {}}

xml <members> <member name="M:zzz.Main"> <vijay> Function Main gets called first </vijay> </member></members>

The earlier program generated an error, since there was no corresponding end tag to the tag 'vijay'. Every tag in XML must necessarily have a start tag and an end tag. We are at liberty to choose any name for the tag. Therefore, we have chosen the name 'vijay'. The compiler creates a tag called 'member' under the tag members, and adds an attribute called 'name'. This attribute is initialized to the name of the C# entity, which is the position where the tag is placed. The value assigned to name is, as follows:

• The letter M, • Followed by a colon separator, • Followed by the name of the class zzz, • Followed by a dot separator • Finally, followed by the name of the function i.e. Main.

Page 205: C#   classes

Thus, it displays M:zzz.Main. The tag 'vijay' and the text enclosed between the start and end tags of 'vijay' subsequently, get enclosed within the members tag.

a.cs/// <mukhi>/// Our first class/// </mukhi>class zzz{/// <vijay>/// Function Main gets called first/// </vijay>public static void Main() {}}

xml <members> <member name="T:zzz"> <mukhi> Our first class </mukhi> </member> <member name="M:zzz.Main"> <vijay> Function Main gets called first </vijay> </member></members>

Now, we have added a tag called 'mukhi' to the class. It is however, not added to the function. So presently, there are two member tags, and each has been assigned a name within the members tag. The value assigned to the member name, which encloses 'mukhi' is, T:zzz. This is created in a manner akin to that of a function, with the exception of the prefix M, which has now been replaced by T. The rationale behind introducing XML tags is that, a program known as an XML Parser can interpret the above XML file, and subsequently, create legible output. But, the prime impediment that we have to confront and surmount is, the reluctance of programmers to document code in the first place.

a.csclass zzz{/// <mukhi>/// Our Instance Variable/// </mukhi>public int i;public static void Main() {/// <vijay>/// Our first variable ii/// </vijay>int ii;}}

Outputa.cs(12,5): warning CS0168: The variable 'ii' is declared but never useda.cs(9,1): warning CS1587: XML comment is not placed on a valid language elementa.cs(6,12): warning CS0649: Field 'zzz.i' is never assigned to, and will always have its default value 0 xml <members> <member name="F:zzz.i"> <mukhi> Our Instance Variable </mukhi> </member></members>

Page 206: C#   classes

Oops! The tags that we give within a function have not been reflected in the XML file. The instance variable i is flagged with an F, but the local variable ii is not reflected in the XML file at all. We have carried this out on purpose, with the intention of demonstrating that it is not permissible to write XML tags within functions. Even though our preceding action would not produce any error, the compiler shall, in any case, ignore our work of art. We will now dwell upon the names or string ids, which the compiler generates for names of entities. They are as follows:

• N stands for a Namespace.• T represents a type, class, interface, struct, enum and delegate. • F denotes a field. • P is indicative of either properties or indexers. • M indicates all methods • E represents an event. • ! is indicative of an error.

a.cs/// <vijay>/// Our first variable ii/// </vijay>namespace aaa{class zzz{public static void Main() {}}}

Outputa.cs(1,1): warning CS1587: XML comment is not placed on a valid language element

We do not enjoy the license to place tags wherever we desire. Thus, a warning is issued when a tag is placed on a namespace, although no error is generated. In the earlier case too, a similar warning was issued, when we had placed the tags in a function.

a.csclass zzz{/// <vijay>/// Our function <c>Main</c> is the starting point /// </vijay>public static void Main() {}}

xml <member name="M:zzz.Main"> <vijay> Our function <c>Main</c> is the starting point </vijay></member>

If we place the tag <c> and </c> around some text in a tag, the tag gets copied verbatim, into the XML file. It is indicative of the fact that the text represents some code. To mark multiple lines of code, the code tag may be used instead. The example tag also works in a similar manner.

a.cs/// <exception cref="System.Exception"> Our exception class </exception>class zzz{public static void Main() {}}

xml <member name="T:zzz"> <exception cref="T:System.Exception"> Our exception class </exception></member>

Page 207: C#   classes

The exception tag works as demonstrated above, with a single difference in that, the attribute cref is inspected for accuracy. This attribute should represent a valid C# entity, otherwise, the following warning is displayed:

/// <exception cref="System.Exceptions"> Our exception class </exception>

Output a.cs(2,22): warning CS1574: XML comment on 'zzz' has cref attribute 'System.Exceptions' that could not be found.

xml <member name="T:zzz"> <exception cref="!:System.Exceptions"> Our exception class </exception></member>

All the tags expounded by us so far, have a similar functionality. This surely must have kindled your curiosity to discover the rationale behind employing so many tags. This is so, because each of these tags, documents different features of the language. Let us assume that we yearn for a bulleted list in our final documentation. We would have to use the following tags to accomplish it:

a.cs/// <list type="bullet">/// <item>/// <description>One</description>/// </item>/// <item>/// <description>Two</description>/// </item>/// </list>class zzz{public static void Main() {}}

xml fragment<member name="T:zzz"> <list type="bullet"> <item> <description>One</description> </item> <item> <description>Two</description> </item> </list> </member>

The attribute type can also be assigned the values of bullet, number or table.Bear in mind, that an external program will interpret the XML tags. Thus, the designers of the language standardized tags for describing different things. If the same tag had been utilized for documenting every feature, a single program would have sufficed to document the entire C# code. We have employed tags such as 'vijay' and 'mukhi' to document our classes. Chaos would reign supreme, if everyone decided to use his or her own names. Appended below is a brief summary of the various tags that can be employed in the XML file:

• The param tag is used to document the parameters to a function.• The paramref tag is used to document a parameter name. • The permission tag is used to document the access allowed to a member. • The remarks tag is used to specify the overall information for a class. • The return tag has been introduced to document the return values of functions. • The seealso tag helps in implementing the 'see also' section at the bottom of help sections. • The summary tag documents all members of a type. • The value tag documents all the properties.