Subodh Personal and Programming tips(C#)

Wednesday, July 13, 2005

Dynamically creating C# and VB code from CodeDOM

using System;
using System.IO;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;

namespace Demo.CodeDOM
{

public class DynamicDOM
{
public DynamicDOM(){}
static void Main(string[] args)
{
CodeCompileUnit compileUnit = new CodeCompileUnit();

CodeNamespace samples = new CodeNamespace("Samples");
samples.Imports.Add( new CodeNamespaceImport("System") );

compileUnit.Namespaces.Add( samples );
CodeTypeDeclaration class1 = new CodeTypeDeclaration("MyClass");
samples.Types.Add(class1);

CodeEntryPointMethod start = new CodeEntryPointMethod();
CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression("System.Console"),
"WriteLine", new CodePrimitiveExpression("Hello World! This code is dymnamically created and compiled") );
start.Statements.Add(cs1);

CodeMethodInvokeExpression cs2 = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("System.Console"),
"ReadLine");
start.Statements.Add(cs2);
class1.Members.Add( start );

Console.WriteLine("Cs File Generated ----- {0}",GenerateCSharpCode(compileUnit));
Console.WriteLine(CompileCSharpCode("HelloWorldcs.cs","HelloWorldcs.exe" ));

Console.WriteLine("VB.Net File Generated ----- {0}",GenerateVBCode(compileUnit));
Console.WriteLine(CompileVBCode("HelloWorldvb.vb","HelloWorldvb.exe" ));
Console.ReadLine();

}
public static String GenerateCSharpCode(CodeCompileUnit compileunit)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeGenerator gen = provider.CreateGenerator();
String sourceFile;
if (provider.FileExtension[0] == '.')
{
sourceFile = "HelloWorldcs" + provider.FileExtension;
}
else
{
sourceFile = "HelloWorldcs." + provider.FileExtension;
}
IndentedTextWriter tw = new IndentedTextWriter( new StreamWriter(sourceFile, false), " ");
gen.GenerateCodeFromCompileUnit(compileunit, tw,new CodeGeneratorOptions());
tw.Close();
return sourceFile;
}
public static bool CompileCSharpCode(String sourceFile, String exeFile)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add( "System.dll" );

cp.GenerateExecutable = true;
cp.OutputAssembly = exeFile;
cp.GenerateInMemory = false;
CompilerResults cr = compiler.CompileAssemblyFromFile(cp, sourceFile);
if(cr.Errors.Count > 0)
{
Console.WriteLine("Errors building {0} into {1}",
sourceFile, cr.PathToAssembly);
foreach(CompilerError ce in cr.Errors)
{
Console.WriteLine(" {0}", ce.ToString());
Console.WriteLine();
}
}
else
{
Console.WriteLine("Source {0} built into {1} successfully.",
sourceFile, cr.PathToAssembly);
}
if (cr.Errors.Count > 0)
{
return false;
}
else
{
return true;
}
}
public static String GenerateVBCode(CodeCompileUnit compileunit)
{
VBCodeProvider provider = new VBCodeProvider();
ICodeGenerator gen = provider.CreateGenerator();
String sourceFile;
if (provider.FileExtension[0] == '.')
{
sourceFile = "HelloWorldvb" + provider.FileExtension;
}
else
{
sourceFile = "HelloWorldvb." + provider.FileExtension;
}
IndentedTextWriter tw = new IndentedTextWriter( new StreamWriter(sourceFile, false), " ");
gen.GenerateCodeFromCompileUnit(compileunit, tw,new CodeGeneratorOptions());
tw.Close();
return sourceFile;
}
public static bool CompileVBCode(String sourceFile, String exeFile)
{
VBCodeProvider provider = new VBCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add( "System.dll" );

cp.GenerateExecutable = true;
cp.OutputAssembly = exeFile;
cp.GenerateInMemory = false;
CompilerResults cr = compiler.CompileAssemblyFromFile(cp, sourceFile);
if(cr.Errors.Count > 0)
{
Console.WriteLine("Errors building {0} into {1}",
sourceFile, cr.PathToAssembly);
foreach(CompilerError ce in cr.Errors)
{
Console.WriteLine(" {0}", ce.ToString());
Console.WriteLine();
}
}
else
{
Console.WriteLine("Source {0} built into {1} successfully.",
sourceFile, cr.PathToAssembly);
}
if (cr.Errors.Count > 0)
{
return false;
}
else
{
return true;
}
}
}
}

Tuesday, July 12, 2005

System.CodeDom

The System.CodeDom namespace contains classes that can be used to represent the elements and structure of a source code document. The classes in this namespace can be used to model the structure of a source code document that can be output as source code in a supported language using the functionality provided by the System.CodeDom.Compiler namespace.

You just wonder why somebody wants to generate assemblies dynamically at runtime and execute. Take ASP.NET for example, it uses the CodeDOM to create object graphs that it compiles into assemblies that can render HTML pages and controls.

using System;
using System.IO;
using Microsoft.CSharp;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;

namespace Demo.CodeDOM
{
class ClsMyDOM
{
[STAThread]
static void Main(string[] args)
{
StreamWriter sw = File.CreateText("RunTimeGen.cs");
sw.WriteLine("using System;");
sw.WriteLine("class ClsDynamicGen{");
sw.WriteLine("public static void Main(){");
sw.WriteLine(@"System.Console.WriteLine(""This code is generated Dynamically"");");
sw.WriteLine("Console.ReadLine();");
sw.WriteLine("}}");
sw.Close();

CSharpCodeProvider csCompiler = new CSharpCodeProvider();
ICodeCompiler iCodeCompiler= csCompiler.CreateCompiler();
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.OutputAssembly = "RuntimeGen.exe";
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.GenerateExecutable = true;

CompilerResults compilerResults= iCodeCompiler.CompileAssemblyFromFile(compilerParams,"RunTimeGen.cs");
DisplayCompilerResults(compilerResults);
Console.ReadLine();
}
public static void DisplayCompilerResults(System.CodeDom.Compiler.CompilerResults cr)
{
// If errors occurred during compilation, output the compiler output and errors.
if( cr.Errors.Count > 0 )
{
for( int i=0; i<cr.Output.Count; i++ )
Console.WriteLine( cr.Output[i] );
for( int i=0; i<cr.Errors.Count; i++ )
Console.WriteLine( i.ToString() + ": " + cr.Errors[i].ToString() );

}
else
{
// Display information about the compiler's exit code and the generated assembly.
Console.WriteLine( "Compiler returned with result code: " + cr.NativeCompilerReturnValue.ToString() );
Console.WriteLine( "Generated assembly name: " + cr.CompiledAssembly.FullName );
if( cr.PathToAssembly == null )
Console.WriteLine( "The assembly has been generated in memory." );
else
Console.WriteLine( "Path to assembly: " + cr.PathToAssembly );

// Display temporary files information.
if( !cr.TempFiles.KeepFiles )
Console.WriteLine( "Temporary build files were deleted." );
else
{
Console.WriteLine( "Temporary build files were not deleted." );
// Display a list of the temporary build files
IEnumerator enu = cr.TempFiles.GetEnumerator();
for( int i=0; enu.MoveNext(); i++ )
Console.WriteLine( "TempFile " + i.ToString() + ": " + (string)enu.Current );
}
}
}

}
}

Monday, July 11, 2005

Size of a .NET object

Each instance of a reference type has two fields maintained by the runtime - a method table pointer and a sync block. These are 4 bytes each on a 32-bit system, making a total of 8 bytes per object overhead. Obviously the instance data for the type must be added to this to get the overall size of the object. So, for example, instances of the following class are 12 bytes each: class MyInt
{
...
private int x;
}
However, note that with the current implementation of the CLR there seems to be a minimum object size of 12 bytes, even for classes with no data (e.g. System.Object).
Values types have no equivalent overhead.

Difference between an event and a delegate

An event is just a wrapper for a multicast delegate. Adding a public event to a class is almost the same as adding a public multicast delegate field. In both cases, subscriber objects can register for notifications, and in both cases the publisher object can send notifications to the subscribers. However, a public multicast delegate has the undesirable property that external objects can invoke the delegate, something we'd normally want to restrict to the publisher. Hence events - an event adds public methods to the containing class to add and remove receivers, but does not make the invocation mechanism public.

Calling Win32 API from a .NET program

Use P/Invoke. This uses similar technology to COM Interop, but is used to access static DLL entry points instead of COM objects. Here is an example of C# calling the Win32 MessageBox
function:

using System;
using System.Runtime.InteropServices;
class MainApp
{
[DllImport("user32.dll", EntryPoint="MessageBox", SetLastError=true, CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);

public static void Main()
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 );
}
}

.NET remoting -- Quick Look

.NET remoting involves sending messages along channels. Two of the standard channels are HTTP and TCP. TCP is intended for LANs only - HTTP can be used for LANs or WANs (internet).

Support is provided for multiple message serializarion formats. Examples are SOAP (XML-based) and binary. By default, the HTTP channel uses SOAP (via the .NET runtime Serialization SOAP Formatter), and the TCP channel uses binary (via the .NET runtime Serialization Binary Formatter). But either channel can use either serialization format.

There are a number of styles of remote access:
SingleCall. Each incoming request from a client is serviced by a new object. The object is thrown away when the request has finished.
Singleton. All incoming requests from clients are processed by a single server object.
Client-activated object. This is the old stateful (D)COM model whereby the client receives a reference to the remote object and holds that reference (thus keeping the remote object alive) until it is finished with it.
Distributed garbage collection of objects is managed by a system called 'leased based lifetime'. Each object has a lease time, and when that time expires the object is disconnected from the .NET runtime remoting infrastructure. Objects have a default renew time - the lease is renewed when a successful call is made from the client to the object. The client can also explicitly renew the lease.

COM Callable Wrapper (CCW)

Dll in .Net and callable in vbscript..

1) CCW.cs
using System;
using System.Runtime.InteropServices;
namespace NMyCCW
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClsCOMServer
{
private string m_strName;
public ClsCOMServer(){}
public void SetName(string strName){m_strName = strName;}
public string GetName(){return m_strName ;}
}
}

2) csc /target:library CCW.cs --- Make a dll

3) regasm ccw.dll /tlb:ccwtlb.tlb /codebase --- Create a tlb.

4) use tlb in vbs---

Dim dotNetObj
Set dotNetObj = CreateObject("NMyCCW.ClsCOMServer")
dotNetObj.SetName ("Subodh")
MsgBox "Name is " & dotNetObj.GetName()

run as --- wscript comclient.vbs

Runtime Callable Wrapper (RCW)

This wrapper turns the COM interfaces exposed by the COM component into .NET-compatible interfaces. For oleautomation interfaces, the RCW can be generated automatically from a type library. For non-oleautomation interfaces, it may be necessary to develop a custom RCW which manually maps the types exposed by the COM interface to .NET-compatible types.
1. create an ATL component which implements the following IDL
2. tlbimp cppcomserver.tlb
3.
using System;
using CPPCOMSERVERLib;
public class MainApp
{
static public void Main()
{
CppName cppname = new CppName();
cppname.SetName( "bob" );
Console.WriteLine( "Name is " + cppname.GetName() );
}
}

4. csc /r:cppcomserverlib.dll csharpcomclient.cs

eg of TLB file:
http://www.andymcm.com/dotnetfaq.htm#1.

import "oaidl.idl";
import "ocidl.idl";
[ object, uuid(EA013F93-487A-4403-86EC-FD9FEE5E6206), helpstring("ICppName Interface"), pointer_default(unique), oleautomation ]
interface ICppName : IUnknown { [helpstring("method SetName")] HRESULT SetName([in] BSTR name); [helpstring("method GetName")] HRESULT GetName([out,retval] BSTR *pName ); }; [ uuid(F5E4C61D-D93A-4295-A4B4-2453D4A4484D), version(1.0), helpstring("cppcomserver 1.0 Type Library") ] library CPPCOMSERVERLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(600CE6D9-5ED7-4B4D-BB49-E8D5D5096F70), helpstring("CppName Class") ] coclass CppName { [default] interface ICppName; }; };

Things that I can't be done in C#

1) Can throw exceptions that are not derived from System.Exception. (in IL its possible)
2) can have non-zero-based arrays, in IL.

ilasm and ildasm

.assembly MyAssembly {}
.class MyApp {
.method static void Main() {
.entrypoint ldstr "Hello, this code is genarated directly from IL..." call void [mscorlib]System.Console::WriteLine(string)
ret
}
}

caspol options

1 ) caspol -lg :- To see the code groups defined on your system
2) caspol -ag 1.3 -site www.mydomain.com FullTrust :- define my own code group
3) caspol -ap samplepermset.xml :-
samplepermset.xml
-
-










-


-


-


-


-


-


-


SamplePermSet
By default this sample permission set is the same as the standard 'Everything' permission set - just edit to suit your needs.

caspol -lg Code Access Security

the code groups defined on your system, run 'caspol -lg' from the command-line

create my own metadata attributes

[AttributeUsage(AttributeTargets.Class)]
public class InspiredByAttribute : System.Attribute
{ public string InspiredBy;
public InspiredByAttribute( string inspiredBy )
{
InspiredBy = inspiredBy;
}
}
[InspiredBy("Andy Mc's brilliant .NET FAQ")]
class CTest { }

class CApp {
public static void Main()
{
object[] atts = typeof(CTest).GetCustomAttributes(true);
foreach( object att in atts )
if( att is InspiredByAttribute )
Console.WriteLine( "Class CTest was inspired by {0}", ((InspiredByAttribute)att).InspiredBy );
}
}

Serialization Issues

1) XmlSerializer is slow --- There is a once-per-process-per-type overhead with XmlSerializer. So the first time you serialize or deserialize an object of a given type in an application, there is a significant delay.

2) XmlSerializer will refuse to serialize instances of any class that implements IDictionary, e.g. Hashtable. SoapFormatter and BinaryFormatter do not have this restriction.

3)
XmlSerializer needs to know in advance what type of objects it will find in an ArrayList. To specify the type, use the XmlArrayItem attibute like this: public class Person
{
public string Name;
public int Age;
}
public class Population
{
[XmlArrayItem(typeof(Person))] public ArrayList People;
}

Else InvalidOperationException is thrown

Should I use XmlSerializer, SoapFormatter or BinaryFormatter?

It depends. XmlSerializer has severe limitations such as the requirement that the target class has a parameterless constructor, and only public read/write properties and fields can be serialized.
However, on the plus side, XmlSerializer has good support for customising the XML document that is produced or consumed. XmlSerializer's features mean that it is most suitable for cross-platform work, or for constructing objects from existing XML documents.

SoapFormatter and BinaryFormatter have fewer limitations than XmlSerializer. They can serialize private fields, for example. However they both require that the target class be marked with the [Serializable] attribute, so like XmlSerializer the class needs to be written with serialization in mind.
Also there are some quirks to watch out for - for example on deserialization the constructor of the new object is not invoked.
The choice between SoapFormatter and BinaryFormatter depends on the application. BinaryFormatter makes sense where both serialization and deserialization will be performed on the .NET platform and where performance is important. SoapFormatter generally makes more sense in all other cases, for ease of debugging if nothing else.

customise the serialization process
XmlSerializer supports a range of attributes that can be used to configure serialization for a particular class. For example, a field or property can be marked with the [XmlIgnore] attribute to exclude it from serialization.
Another example is the [XmlElement] attribute, which can be used to specify the XML element name to be used for a particular property or field.
Serialization via SoapFormatter/BinaryFormatter can also be controlled to some extent by attributes. For example, the [NonSerialized] attribute is the equivalent of XmlSerializer's [XmlIgnore] attribute.
Ultimate control of the serialization process can be acheived by implementing the the ISerializable interface on the class whose instances are to be serialized.

What is the lapsed listener problem?

The lapsed listener problem is one of the primary causes of leaks in .NET applications. It occurs when a subscriber (or 'listener') signs up for a publisher's event, but fails to unsubscribe.
The failure to unsubscribe means that the publisher maintains a reference to the subscriber as long as the publisher is alive.
For some publishers, this may be the duration of the application.

This situation causes two problems. The obvious problem is the leakage of the subscriber object. The other problem is the performance degredation due to the publisher sending redundant notifications to 'zombie' subscribers.

There are at least a couple of solutions to the problem.
The simplest is to make sure the subscriber is unsubscribed from the publisher, typically by adding an Unsubscribe() method to the subscriber. Another solution, documented here by Shawn Van Ness, is to change the publisher to use weak references in its subscriber list.

creating Application Domain

AppDomains are usually created by hosts. Examples of hosts are the Windows Shell, ASP.NET and IE. When you run a .NET application from the command-line, the host is the Shell. The Shell creates a new AppDomain for every application.
AppDomains can also be explicitly created by .NET applications. Here is a C# sample which creates an AppDomain, creates an instance of an object inside it, and then executes one of the object's methods:

using System;
using System.Runtime.Remoting;
using System.Reflection;
public class CAppDomainInfo : MarshalByRefObject
{ public string GetName()
{
return AppDomain.CurrentDomain.FriendlyName;
}
}
public class App
{ public static int Main()
{
AppDomain ad = AppDomain.CreateDomain( "Andy's new domain" );
CAppDomainInfo adInfo = (CAppDomainInfo)ad.CreateInstanceAndUnwrap Assembly.GetCallingAssembly().GetName().Name, "CAppDomainInfo" );
Console.WriteLine( "Created AppDomain name = " + adInfo.GetName() );
return 0;
}
}

Tuesday, July 05, 2005

Interface Rules

1) All method declarations are public within an interface. This is the default access modifier, which means you can omit the modifier in the declaration. As a matter of fact, if you code a modifier like protected, private, or even public, you will get a compiler error.
2)
An interface cannot contain data declarations. However, you can declare properties:int amountOfFood
{
get;
set;
}
3)
An interface can inherit from another interface. You may wish to add more functionality to the interface and keep the original less-specific interface as it is
interface ISnakeFeedable: IFeedable
{
void FeedSnake();
}

Monday, July 04, 2005

Tips on System.Object

1) Whenever you override the Equals() method, you should also override the GetHashCode() method. If you don’t, you may find that the hash codes (unique numbers that identify objects) are unequal

2) There are two implementations of the Equals() method in the Object class. Their syntax is as follows: USe any 1 of them depending on choice:

public virtual bool Equals(object obj)
public virtual bool Equals(object objA, object objB)

Eg:
In the comparison line (a == a1), we are testing the equality of the reference variables, which are identical. However, the intent of the second test line (a.Equals(a2)) is to test the data of the two objects for equality.

Animal a = new Animal();
Animal a1 = a;
Animal a2 = new Animal();
if (a == a1) // the equals operator compares the reference variables
System.Console.WriteLine ("The references are equal");
if (a.Equals(a2)) // Compares Objects
System.Console.WriteLine ("The objects are equal");
else
System.Console.WriteLine ("The objects are not equal");

Constructors

1) Constructors are not inherited by derived classes
2) But if a constructor—any constructor— is coded then the no-parameter constructor must be coded if needed.

Friday, July 01, 2005

tips on switch case -- no fall through in C#

Here’s a list of the C#-specific rules for using the switch statement:
A switch block consists of zero or more cases.
The switch expression can be of these types: sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or enum.
There can only be one default label, and it is optional. If none of the case statements match the switch value, execution is transferred to the default label.
Execution of the switch section is not permitted to "fall through" as is the case in C, C++, and Java. This means that you must code the break statement at the end of each case evaluation.
Since execution is not permitted to fall through, you can put the case statements in any order.
A case selection can have multiple labels as in the following:case 12 :
case 42 :
default :
do something here;
break;
Note
Java and C++ programmers—look out for the no “fall through” rule. You will be tempted to use it, and C# will not allow it.

difference between readonly and const

The big difference between constants and readonly variables is that a readonly variable can receive its value at run time and a constant receives its value at compile time.

Effectively, it produces the same result—you end up with a variable that cannot change.

The following example declares a constant to hold the date March 12, 2002. That date can never change—it is compiled into the program. The second declaration is for a readonly variable that is initialized at run time to the current date.
const string TheDate = "03/09/2002";
public static readonly string TheDateRO = new DateTime().ToString();

Must see --- Finalize(), dispose(), close()

The Finalize() method is similar to the traditional destructor in C++. Unfortunately, Finalize() is not deterministic.
If a lot of objects need to run Finalize(), there will be a high load on the garbage collector, which in turn results in a major degradation in application performance with no measurable positive impact on the garbage collection process.

It is better to use a declared destructor rather than Finalize(). Declaring a destructor (~classname) is more in line with the C++ culture, but it still only calls the Finalize(), so the issue of deterministic destruction is still there.

The C# compiler will actually convert the declared destructor to a call to the Finalize() method. The Finalize() method is called recursively for all of the instances in the inheritance chain, from the most derived to the least derived.
it is better to use the Close() and Dispose() methods.

Noteopad - Searching and replacing

private void ButtonFindNext_Click(object sender,EventArgs eArgs)
{
int Pos=-1;
if(IsDirectionDownward)
{
if(TextArea.SelectionLength==0)
Pos=TextArea.Text.IndexOf(TextToFind,TextArea.SelectionStart);
else
Pos=TextArea.Text.IndexOf(TextToFind,TextArea.SelectionStart+TextArea.SelectionLength);
}
else
{
if(TextArea.SelectionStart>0)
Pos=TextArea.Text.LastIndexOf(TextToFind,TextArea.SelectionStart-1);
}
if(Pos!=-1)
{
TextArea.SelectionStart=Pos;
TextArea.SelectionLength=TextToFind.Length;
if(sender!=null)
((Control)sender).Focus();
}
else
{
MessageBox.Show("Cannot Find: \"" + TextToFind + "\"");
}
}



private string ReplaceString(string StrSource,string StrFind,string StrReplace)
{
int iPos=StrSource.IndexOf(StrFind);
String StrReturn="";
while(iPos!=-1)
{
StrReturn+=StrSource.Substring(0,iPos)+StrReplace;
StrSource=StrSource.Substring(iPos+StrFind.Length);
iPos=StrSource.IndexOf(StrFind);
}
if(StrSource.Length>0)
StrReturn+=StrSource;
return StrReturn;
}

Notepad - opening a file, Saving a file

private void Open_Click(object sender, System.EventArgs e)
{
try
{
openFileDialog1.ShowDialog();
fileName = openFileDialog1.FileName;
StreamReader stReader = new StreamReader(fileName);
TextArea.Text = stReader.ReadToEnd();
checkText = false;
stReader.Close();
this.Text = fileName + " - Notepad";
fileName = "";
}
catch{
}
}




private void saveFile()
{
if ( fileName == "")
{
saveFileDialog1.Filter = "TEXT Documents. (*.txt)*.txtAll files*.*";
DialogResult resVal = saveFileDialog1.ShowDialog();
if ( resVal == DialogResult.Cancel )
{
return;
}
fileName = saveFileDialog1.FileName;
this.Text = fileName + " - Notepad";
}
try
{
StreamWriter stWriter = new StreamWriter(fileName);
stWriter.WriteLine(TextArea.Text);
stWriter.Flush();
stWriter.Close();
checkText = false;
}
catch
{
MessageBox.Show(this, "The file "+fileName+" is Read only. \n\n File Could not saved.", "Notepad", MessageBoxButtons.OK,MessageBoxIcon.Exclamation);
return;
}
}

inserting dynamic controls and events

private void Page_Load(object sender, System.EventArgs e)
{
MyTable = new Table();
MyTable.GridLines = GridLines.Both;
for (int i=0;i<5;i++)
{
InitArrayList();
TableRow tr = new TableRow();
for(int j=0;j<3;j++)
{
TableCell td = new TableCell();
if(j==0)
{
TextBox t = new TextBox();
t.ID = "txt_" + i.ToString() + j.ToString();
td.Controls.Add(t);
}
else
{
td.Text = "hello" + i.ToString() + " " + j.ToString();;
}
tr.Cells.Add(td);
}
MyTable.Rows.Add(tr);
}
Panel1.Controls.Add(MyTable);
}








void IterateControls(Control parent)
{
foreach (Control child in parent.Controls)
{ if (child.GetType().ToString().Equals ("System.Web.UI.WebControls.TextBox") && child.ID.IndexOf("txt_") == 0)

{ TextBox textbox = (TextBox)child;

textbox.Text = "You Got It !";
}
if (child.Controls.Count > 0)
{
IterateControls(child);
} } }
private void Button1_Click(object sender, System.EventArgs e) { IterateControls(this); } }