Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Friday 25 November 2011

Deploy/Use assemblies which require Unsafe/External Access with CLR and T-SQL

 

What is an Unsafe Assembly?

Assemblies which are built using normal computational functions are considered as safe assemblies. But when assemblies do external operations such as reading file information, creating files, etc.… they are categorized as unsafe/external assemblies.

**Visual studio creates safe assemblies by default.

We will create an assembly which access the external file system, so that it will need external access. We will create a simple function which will return the file size for a given file, using the ‘FileInfo’ class.

using System;
using Microsoft.SqlServer.Server;
using System.IO;

public partial class UserDefinedFunctions {
[SqlFunction]
public static long GetFileSize(string FileName) {
FileInfo fi = new FileInfo(FileName);
return fi.Length;
}

};



Now right click the project (from the solution explorer) and go the properties tab. Form the ‘Database’ tab select the permission level to ‘External’. (Default value is ‘Safe’)

img_scr_001

Open MS SQL Server Management Studio (Run it as Administrator since you are going to assign permission to the current user), and log in as a different user than the one you are trying to provide access permission (For this example I am logging as ‘sa’). Execute the following script.


use master;
grant external access assembly to [Domain\UserID];
use SampleCLR;


Use the appropriate values for ‘Domain’ and ‘UserID’. (And ‘SampleCLR’ is the database that I will be using)

**Please note that this is a server wide permission. Therefore user can create any external assembly in any database on the SQL Server.

Above script will grant permission the user to create external assemblies on the executed server. But it is not sufficient. The database should be allowed to have external access assemblies.

There are two methods of doing so.


  1. Making the database trusted.
  2. Using sign assemblies.

Method 1 ~ Making Database Trusted

Execute the following script (using Management Studio) in order to make the database trusted.

alter database SampleCLR set trustworthy on;



And if you inspect the database properties, you can see that the database’s ‘Trustworthy’ property value is changed to ‘True’

img_scr_002

Now go to visual studio and deploy the solution. It will succeed without any issues.

However if you try to deploy without doing the above mentioned steps, you will get the following error.


CREATE ASSEMBLY for assembly <AssemblyName> failed because assembly <AssemblyName> is not authorized for PERMISSION_SET = EXTERNAL_ACCESS. The assembly is authorized when either of the following is true: the database owner (DBO) has EXTERNAL ACCESS ASSEMBLY permission and the database has the TRUSTWORTHY database property on; or the assembly is signed with a certificate or an asymmetric key that has a corresponding login with EXTERNAL ACCESS ASSEMBLY permission


We will check the deployed assembly by executing the following script. (I have a file on my D:\ drive with the mentioned name.)


select dbo.GetFileSize(N'D:\data.csv')

img_scr_003


img_scr_004



And if you try to execute the function without making the database trusted you will get the following error


Msg 10314, Level 16, State 11, Line 1
An error occurred in the Microsoft .NET Framework while trying to load assembly id 65540. The server may be running out of resources, or the assembly may not be trusted with PERMISSION_SET = EXTERNAL_ACCESS or UNSAFE. Run the query again, or check documentation to see how to solve the assembly trust issues. For more information about this error:
System.IO.FileLoadException: Could not load file or assembly 'sqlclrproject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An error relating to security occurred. (Exception from HRESULT: 0x8013150A)
System.IO.FileLoadException:
   at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
   at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.Load(String assemblyString)


Method 2 ~ Using Sign Assemblies

Set the trustworthy to false by using the following script (You only have to do this if you have made the database a trusted one in the previous example, and I am keeping it false for illustrated purpose)


alter database SampleCLR set trustworthy off;



In order to sign an assembly we need a public/private key file (.snk file). We will create one using the ‘sn.exe’.

img_scr_005


sn -k "D:\Sample CLR\SampleCLRKey.snk"



And sign the assembly using the key file that we have created now.

To sign an assembly: go to project properties => select the ‘Signing’ tab and check the ‘Sign the assembly’ check box and browse and select the created file. Save the project.

In order to deploy the assembly,


  1. Need to create an asymmetric key using the key file which we have created in the SQL Server
  2. Need to create a login using that asymmetric key
  3. Giving that login the permission for external access assemblies

img_scr_006


Use the following script to create the asymmetric key using SQL Server Management Studio. (** Please note that the key should be created on the master database)


use master;
create asymmetric key CLRExtensionKey
from file = 'D:\Sample CLR\SampleCLRKey.snk'
encryption by password = '@Str0ngP@$$w0rd'



Now create the login using the above created key (*Please note that the login should be created on the database which you want to publish the assembly to)


use SampleCLR;
create login CLRExtensionLogin from asymmetric key CLRExtensionKey;



Give the login permission for external access assemblies.


use master;
grant external access assembly to CLRExtensionLogin;



Now go to visual studio and deploy the solution. And you can use the following statement which we used in Method 1.


select dbo.GetFileSize(N'D:\data.csv')


img_scr_007

Tuesday 22 November 2011

Using CLR functions with T-SQL


Why use CLR?
Before using CLR, you should question ‘Why’. When using CLR functions within T-SQL, you have to maintain two different programming environments, unless what you gain worth more than having it.
We need to use the CLR functions when occasions such as:
When we have to access system resources like file system or network (**Extended stored procedures can do the same. But they are deprecated and will be removed from future versions of SQL)
Or when the business logics are too complex to write in T-SQL, which can be easily done using .net languages (Reusing the logics already written, without duplicating them using T-SQL)

To illustrate this I will create a CLR function which will create a text file and append the text which is passed from a T-SQL statement.
Open Visual studio and create a new ‘SQL Server Project’
img_scr_001

Next dialog you will be prompted to select the database which you plan to deploy your extension. You can either select from existing or connect to a different one.
img_scr_002

In next screen you will be prompted, whether you want to debug your .net coding. Select ‘yes’ or ‘no’ depending on your requirement.
img_scr_003

Right click on the newly created project and add new user-defined- function
img_scr_004
I have named it as ‘WriteToTextFileSample’.
img_scr_005
Use the following code:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

using System.IO;
using System.Text;

public partial class UserDefinedFunctions {
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString WriteToTextFileSample(string filename, string data) {
FileStream fs = File.Open(filename, FileMode.Append);
byte[] b = new UTF8Encoding(true).GetBytes(data);

fs.Write(b, 0, data.Length);
fs.Close();

return new SqlString("Completed");
}
};

**The functions should be 'public static' and all the functions should have '[Microsoft.SqlServer.Server.SqlFunction]' attribute or you will not be able to call it from SQL.


Go to the project properties and change the ‘Permission Level’ to ‘Unsafe’ in Database section. (This is only required if you are performing tasks such as writing to files, accessing system related resources. You do not need this if you are performing calculations, etc..)

Save your project. Before deploying this you have to enable the ‘clr  enable’ to ‘1’. Use the following T-SQL statement to do so.

sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO


Once executed, deploy the project (Build—> Deploy Solution). You will be prompted with the SQL credentials.

img_scr_006



Once it’s deployed successfully use the following sample statement to use the function which we have created.

select dbo.WriteToTextFileSample(
N'D:\SampleCLRText.txt',
N'Hello from SQL !!!'
)


img_scr_007

Tuesday 9 August 2011

Change the data source of a Crystal Report at Run-time using C#

It’s a known fact, that in Crystal Reports, if you try to display details, using data in a SQL Server Database other than the one that you’ve used to design the report, either you have to set the database location (or refresh the report). This is a common issue that development team face when the reports are being deployed to the production environment.

But this can be prevented using the following method.

Following namespaces are required.

using CrystalDecisions.CrystalReports.Engine;

using CrystalDecisions.Shared;



And we have to assign the required SQL server information to each table/view of the report, sub reports and the report viewer.

Use the following code:


SQLReport report = new SQLReport();

//Get SQL Server Details
string zServer = @"SERVER_NAME";
string zDatabase = @"DATABASE";
string zUsername = @"USER";
string zPassword = @"PASSWORD";

ConnectionInfo ciReportConnection = new ConnectionInfo();

ciReportConnection.ServerName = zServer;
ciReportConnection.DatabaseName = zDatabase;
ciReportConnection.UserID = zUsername;
ciReportConnection.Password = zPassword;

//Assign data source details to tables

foreach (Table table in report.Database.Tables) {
table.LogOnInfo.ConnectionInfo = ciReportConnection;
table.ApplyLogOnInfo(table.LogOnInfo);
}

foreach (ReportDocument subrep in report.Subreports) {
foreach (Table table in subrep.Database.Tables) {
table.LogOnInfo.ConnectionInfo = ciReportConnection;
table.ApplyLogOnInfo(table.LogOnInfo);
}
}

//Assign data source details to the report viewer
if (this.crystalReportViewer1.LogOnInfo != null) {
TableLogOnInfos tlInfo = this.crystalReportViewer1.LogOnInfo;
foreach (TableLogOnInfo tbloginfo in tlInfo) {
tbloginfo.ConnectionInfo = ciReportConnection;
}
}


crystalReportViewer1.ReportSource = report;
crystalReportViewer1.Refresh();

Monday 8 August 2011

Invoke a custom method when Crystal Report Viewers’ print button is clicked / Add custom button to Crystal Report Viewer Toolbar

If you are developing applications which includes reporting with Crystal Reports, you may have noticed that it’s not possible to invoke a custom method, when the user prints the report. However this was something which was easily implemented in Crystal Reports 8/8.5 but removed from latter versions.

But there’s a workaround for this. In this example I will show you how to invoke a method in our client application, when the print button of the report viewer is clicked.

In order to do that we have to add our custom method to the report viewers’ print buttons’ print action.

Create a new windows application.

Add another form to the project and name it as ‘CustomReportViewer.cs’.

Add a Crystal Report viewer to a newly created form. (If the crystal report viewer is not available in the toolbox, please add it to the toolbox first)

img_scr_001_a

img_scr_002

Add new report to the project and name it as ‘SampleReport.rpt’.

img_scr_003

Now add the following code to the ‘CustomReportViewer’ class

public delegate void CustomPrintDelegate();


Add the following property.


public Delegate CustomPrintMethod { get; set; }


Add this additional code to the initialization method.


foreach (Control control in crystalReportViewer1.Controls) {
if (control is System.Windows.Forms.ToolStrip) {

//Default Print Button
ToolStripItem tsItem = ((ToolStrip)control).Items[1];
tsItem.Click += new EventHandler(tsItem_Click);

//Custom Button
ToolStripItem tsNewItem = ((ToolStrip)control).Items.Add("");
tsNewItem.ToolTipText = "Custom Print Button";
tsNewItem.Image = Resources.CustomButton;
tsNewItem.Tag = "99";
((ToolStrip)control).Items.Insert(0, tsNewItem);
tsNewItem.Click += new EventHandler(tsNewItem_Click);
}
}


Using the above coding we can find out the print button of the report viewers’ too strip. And the 1st item is for the print button. (I have found this out from its ToolTipText).


How ever you can add your own button if you like or you can use the existing print button. Both options are illustrated.


Add the following methods.



void tsNewItem_Click(object sender, EventArgs e) {
if (CustomPrintMethod != null) {
CustomPrintMethod.DynamicInvoke(null);
}
}

void tsItem_Click(object sender, EventArgs e) {
if (CustomPrintMethod != null) {
CustomPrintMethod.DynamicInvoke(null);
}
}


Here is the complete coding of the ‘CustomReportViewer’ class.


using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;

namespace PrintDelegateMethod {
public partial class CustomReportViewer : Form {

public delegate void CustomPrintDelegate();

public Delegate CustomPrintMethod { get; set; }

public CustomReportViewer() {
InitializeComponent();

foreach (Control control in crystalReportViewer1.Controls) {
if (control is System.Windows.Forms.ToolStrip) {

//Default Print Button
ToolStripItem tsItem = ((ToolStrip)control).Items[1];
tsItem.Click += new EventHandler(tsItem_Click);

//Custom Button
ToolStripItem tsNewItem = ((ToolStrip)control).Items.Add("");
tsNewItem.ToolTipText = "Custom Print Button";
tsNewItem.Image = Resources.CustomButton;
tsNewItem.Tag = "99";
((ToolStrip)control).Items.Insert(0, tsNewItem);
tsNewItem.Click += new EventHandler(tsNewItem_Click);
}
}
}

void tsNewItem_Click(object sender, EventArgs e) {
if (CustomPrintMethod != null) {
CustomPrintMethod.DynamicInvoke(null);
}
}

void tsItem_Click(object sender, EventArgs e) {
if (CustomPrintMethod != null) {
CustomPrintMethod.DynamicInvoke(null);
}
}

private void CustomReportViewer_Load(object sender, EventArgs e) {
SampleReport report = new SampleReport();
crystalReportViewer1.ReportSource = report;
crystalReportViewer1.Refresh();
}
}
}


 


Add the following delegate to your calling class


public delegate void PrintDelegate();


Add the following method. This is the method that we want to invoke when the print button or the custom button is clicked.


private void CustomPrintMethod() {
MessageBox.Show("Custom Print Method");
}


And a button and the following click event code.


private void button1_Click(object sender, EventArgs e) {
CustomReportViewer viewer = new CustomReportViewer();
PrintDelegate mymethod = new PrintDelegate(CustomPrintMethod);
viewer.CustomPrintMethod = mymethod;
viewer.Show();

}


The complete source of the calling form:


using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace PrintDelegateMethod {
public partial class Form1 : Form {
public delegate void PrintDelegate();

public Form1() {
InitializeComponent();

}

private void CustomPrintMethod() {
MessageBox.Show("Custom Print Method");
}


private void button1_Click(object sender, EventArgs e) {
CustomReportViewer viewer = new CustomReportViewer();
PrintDelegate mymethod = new PrintDelegate(CustomPrintMethod);
viewer.CustomPrintMethod = mymethod;
viewer.Show();

}


}
}


Now if you run the project, you can get a similar screen shown below. And please note that I have added a resource file named ‘Resources’ and added an image named ‘CustomButton’

img_scr_011_a

And if you click either of the buttons, your custom method will be invoked. The default print method will be executed only when the print button is clicked.

img_scr_012

Thursday 14 July 2011

Extension Methods in C#

                                            
As name implies extension methods allow existing classes to be extended without using inheritance or changing class’s source code.

Some important points when using extension methods:

•    You cannot use extension methods to override existing methods
•    An extension method with the same name and signature as an instance method must not be called
•    The concept must not be applied to fields, properties or events
•    Do not overuse

As an example, we’ll create an extension method to existing ‘String’ class. We’ll implement the functionality of converting a string to a proper case. (Any given string will be converted to lower case, having the first letter of each word in upper case. E.g.: ‘this is sample text’ will be converted to ‘This Is Sample Text’)

Add a class and add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ExtensionMethods {
public static class SampleExtensionMethods {
public static string ToProper(this string zSource) {
StringBuilder sb = new StringBuilder();

string zTemp = zSource.ToLower();
List<string> data = zTemp.Split(new char[] { ' ' }).ToList<string>();
if (data.Count > 0) {
foreach (string element in data) {
if (element.Trim().Length > 0) {
sb.Append(element.Trim().Substring(0, 1).ToUpper() + element.Trim().Substring(1, element.Trim().Length - 1) + " ");
}
}
}

return sb.ToString().Trim();
}
}
}



please note that the class that the class should be static and the method should be public (Unless you are writing it within the same calss which you intend to use it). And also in input the parameter I have mentioned 'this'. This will tell the compiler to add the following method to the 'String' class.


Before using include the namspace. (I have used 'ExtensionMethods' as the namespace)


using ExtensionMethods;


And you can use the method we created using this syntax :


SomeStringVar.ToProper()


E.g:


using System;
using System.Text;
using ExtensionMethods;

namespace SampleConsoleApplicationA {
class SampleExtMethod {
static void Main(string[] args) {
string zSample = "this is sample text";
Console.WriteLine("Input : " + zSample);
Console.WriteLine("Output : " + zSample.ToProper());
Console.ReadLine();


}
}
}



And if you run the application you can get the below output (I have used a console application to illustrate this)


Extension Method

Delegates in C#

                                            
What's a delegate

A delegate is a type that safely wraps/encapsulate a  method. It is similar to 'function pointers' in C/C++, but it's type safe.

 

When to use a delegate

Use a delegate when :

  • An eventing design pattern is used.
  • It is desirable to encapsulate a static method.
  • The caller has no need access other properties, methods, or interfaces on the object implementing the method.
  • Easy composition is desired.
  • A class may need more than one implementation of the method.

There was a good example given on a book that I recently referenced.
'Consider your will—your last will and testament. It’s a set of instructions—“pay the bills, make a donation to charity, leave the rest of my estate to the cat,” for instance. You write it before your death, and leave it in an appropriately safe place. After your death, your attorney will (you hope!) act on those instructions.'

In order to a delegate to proceed, following four things need to happen:
* The delegate type needs to be declared.
* There must be a method containing the code to execute.
* A delegate instance must be created.
* The delegate instance must be invoked.

Declaring delegate type
Declaration of a delegate type specifies what kind of action can be represented by instances of the type.

E.g :

delegate void MySampleDelegate(string zParameter)



If we are creating an instance of the above delegate, we should have a method with one string parameter and void return type.



Method containing the code to execute
There should be a method which matches the signature of the delegate. Method can be either static or non static. I will take two examples to illustrate a static method and a non static method. I will use the following two methods in the 'DelegateClass' class.


E.g :


public class DelegateClass {
public static void MySampleDelegateMethodStatic(string zParameter) {
Console.WriteLine("From static method. Parameter : {0}", zParameter);
}

void MySampleDelegateMethodNonStatic(string zParameter) {
Console.WriteLine("From non static method. Parameter : {0}", zParameter);
}
}



Creating a delegate instance
When creating delegate instances static methods can  be passed directly. But for non static methods an instance should be created and passed that to the delegate.


E.g :    [Static Method]


MySampleDelegate delMethodA = new MySampleDelegate(DelegateClass.MySampleDelegateMethodStatic);


E.g :    [Non Static Method]


DelegateClass deligateClass = new DelegateClass();
MySampleDelegate delMethodB = new MySampleDelegate(deligateClass.MySampleDelegateMethodNonStatic);


Or


MySampleDelegate delMethodB = new MySampleDelegate(new DelegateClass().MySampleDelegateMethodNonStatic);


Invoking delegate method
It's a matter of calling the Invoke method on the delegate instance.


E.g :


delMethodA.Invoke("Parameter A");
delMethodB.Invoke("Parameter B");



Here is the complete source code for the above example. I have used a console application to illustrate this :


using System;
using System.Text;


namespace SampleConsoleApplicationA {

delegate void MySampleDelegate(string zParameter);

class Program {
static void Main(string[] args) {

MySampleDelegate delMethodA = new MySampleDelegate(DelegateClass.MySampleDelegateMethodStatic);
MySampleDelegate delMethodB = new MySampleDelegate(new DelegateClass().MySampleDelegateMethodNonStatic);

delMethodA.Invoke("Parameter A");
delMethodB.Invoke("Parameter B");

Console.ReadLine();
}
}

public class DelegateClass {
public static void MySampleDelegateMethodStatic(string zParameter) {
Console.WriteLine("From static method. Parameter : {0}", zParameter);
}

public void MySampleDelegateMethodNonStatic(string zParameter) {
Console.WriteLine("From non static method. Parameter : {0}", zParameter);
}
}
}






And when you run your application you will get the following output:


Delegate Sample


Instead of using separate delegate instances, you can combine and execute delegates. We have to use the 'Combine' method in Delegate class. To illustrate this I will alter my 'DelegateClass' as shown below :


public class DelegateClass {

public void MySampleMethodA(string zParameter) {
Console.WriteLine("MySampleMethodA. Parameter : {0}", zParameter);
}

public void MySampleMethodB(string zParameter) {
Console.WriteLine("MySampleMethodB. Parameter : {0}", zParameter);
}

public void MySampleMethodC(string zParameter) {
Console.WriteLine("MySampleMethodC. Parameter : {0}", zParameter);
}

}



To create instances and invoke :



MySampleDelegate[] mySampleDelegates = new MySampleDelegate[]{
new MySampleDelegate(new DelegateClass().MySampleMethodA),
new MySampleDelegate(new DelegateClass().MySampleMethodB),
new MySampleDelegate(new DelegateClass().MySampleMethodC)
};

MySampleDelegate sample = (MySampleDelegate)Delegate.Combine(mySampleDelegates);
sample.Invoke("Parameter X");



Or

MySampleDelegate D, deligateA, deligateB, deligateC;

deligateA = new DelegateClass().MySampleMethodA;
deligateB = new DelegateClass().MySampleMethodB;
deligateC = new DelegateClass().MySampleMethodC;

D = deligateA + deligateB + deligateC;

D("Parameter X");



 


The complete source code :


using System;
using System.Text;


namespace SampleConsoleApplicationA {

delegate void MySampleDelegate(string zParameter);

class Program {
static void Main(string[] args) {

MySampleDelegate[] mySampleDelegates = new MySampleDelegate[]{
new MySampleDelegate(new DelegateClass().MySampleMethodA),
new MySampleDelegate(new DelegateClass().MySampleMethodB),
new MySampleDelegate(new DelegateClass().MySampleMethodC)
};

MySampleDelegate sample = (MySampleDelegate)Delegate.Combine(mySampleDelegates);
sample.Invoke("Parameter X");


/*
MySampleDelegate D, deligateA, deligateB, deligateC;

deligateA = new DelegateClass().MySampleMethodA;
deligateB = new DelegateClass().MySampleMethodB;
deligateC = new DelegateClass().MySampleMethodC;

D = deligateA + deligateB + deligateC;

D("Parameter X");

*/

Console.ReadLine();
}
}

public class DelegateClass {

public void MySampleMethodA(string zParameter) {
Console.WriteLine("MySampleMethodA. Parameter : {0}", zParameter);
}

public void MySampleMethodB(string zParameter) {
Console.WriteLine("MySampleMethodB. Parameter : {0}", zParameter);
}

public void MySampleMethodC(string zParameter) {
Console.WriteLine("MySampleMethodC. Parameter : {0}", zParameter);
}

}
}



 


An when you run the application, you will get the following output:


Delegate Sample B

Tuesday 18 January 2011

Designing a windows form where size exceeds your screen’s resolution

Some times we need to design forms in our  applications, beyond the size of our development machines screen resolution. But the worse case is, our development environment does not allow us to resize our forms beyond our screen resolution. (Screen resolution – Borders to be exact).

One method is to do the development on the client resolution. But it’s not going to work all the time, if we have to do the development and send it across to a different physical location.

The other method is to insert a ‘Panel’ to the form, change it size to the one at the clients end. The one I am working at has the resolution of ‘1440 X 900’. But I want to make a form which fits for ‘1920 X 1200’. But I will change the Panel’s size to a bit less than ‘1920 X 1200’. Because I have to leave space for the borders and the scroll bars. So I would make it somewhere around ‘1870 X 1150’

And change the following form properties also :

  • AutoScaleMode = None
  • AutoScroll = True
  • AutoSize = True
  • WindowState = Maximized

 

And you can note that there are two scroll bars available at the design time, which you can scroll and place controls beyond your screens resolution. And when run on clients environment, the form will  maximize and since the panel is bit smaller than the actual screen size, the scroll bars will not be visible.

screen_01

Thursday 9 December 2010

Constructor Chaining in C#

 

What’s ‘Constructor Chaining’ ?

Constructor Chaining is an approach where a constructor calls another constructor in the same or base class.

This is very handy when we have a class that defines multiple constructors. Assume we are developing a class call ‘Student’. And this class consist of 3 constructors. On each constructer we have to validate the students id and categorize him/her. So if we do not use the constructor chaining approach, it would be something similar to the one shown below:

screen_01

Even the above approach solve our problem, it duplicate our code. (We are assigning a value to ‘_id’ in all constructors). This where constructor chaining is very useful. It will eliminate this problem. This time we only assign values only in the constructor which consist most number of parameters. And we call that constructor, when other two constructers are called.

class Student {
string _studentType = "";
string _id = "";
string _fName = "";
string _lName = "";

public Student(string id)
: this(id, "", "") {

}

public Student(string id, string fName)
: this(id, fName, "") {

}

public Student(string id, string fName, string lName) {
//Validate logic.....
_studentType = "<student_type>";

_id = id;
_fName = fName;
_lName = lName;
}
}


**Please note: If you do not specify anything [In this example we used ‘this’), it will be consider that we are calling the constructor on the base class. And it’s similar to using ‘: base(…)’]

Friday 3 December 2010

Show properties of a class on Property Grid

When developing user controls, usually we use Booleans, Integers, Strings, etc.. as data types of the attributes of the control. But sometimes, we have to use structures or other classes as attributes. So when we use those, we should be able to change or browse the attributes of those structures or classes.

Open visual studio IDE and create a new Windows Forms Application type project.

Add a new class and name it as ‘MyCustomClass’. And define two properties. One integer and a string type.

public class MyCustomClass {

public int MyIntProperty { get; set; }
public string MyStringProperty { get; set; }

public override string ToString() {
return "...";
}
}







**Please note that I have overridden the ‘ToString’ method. Because this what will be shown on the property grid when the properties are collapsed.



Now add a new user control to the project and name it as ‘MyCutomUsercontrol’. And create three properties. One integer, string and MyCustomClass. And use ‘[TypeConverter(typeof(ExpandableObjectConverter))]’ attribute on the third property. The syntax should be:






public partial class MyCutomUsercontrol : UserControl {
public MyCutomUsercontrol() {
InitializeComponent();
}

private MyCustomClass _MyCustomClass = new MyCustomClass();

public int Property1 { get; set; }
public string Property2 { get; set; }

[TypeConverter(typeof(ExpandableObjectConverter))]
[EditorBrowsable(EditorBrowsableState.Always)]
public MyCustomClass Property3 {
get {
return _MyCustomClass;
}
set {
_MyCustomClass = value;
}
}

}





 



Now build the solution. And add it a windows form. And on the property grid you can see your custom controls properties.



screen_1