DataSetExtender
---------------------------------------------------------------
Demonstrates DataSet Extender and constraints defined in an arbitrary .NET programming language.

This sample demonstrates the usage of an ADO.Net typed DataSet 
Extender and explains the usage of constraints defined 
in an arbitrary .NET programming language (represented by 
ConstraintsForm form)

NOTE: Immediately after opening this solution, before opening 
any of its forms, you should rebuild the solution. Otherwise, the form 
cannot be opened.

DataSet Extender is just a schema which sits on top of an ADO.NET typed 
DataSet schema and provides additional properties to the DataTable and 
DataColumn objects of DataSet. These additional properties allows you to
define additional functionality aspects of C1DataViewSet.

DataSet Extender is represented by its root DataSetExtender class and 
appears as a global project item. Usually a single DataSet Extender
is necessary for a specific typed DataSet.

DataSet Extender creation

To create a DataSet Extender choose the
"Project/Add New Item/DataSet Extender" menu item from 
VS IDE. In the "Name" textbox enter "NWindDataSetExtender" 
and press the "Add" button. NWindDataSetExtender is created as a global
project item and its designer automatically appears in a separate
window of the VS IDE.
After this, select the DataSet Extender root node in the designer and set its
DataSet property value to "NwindDataSet". The NWindDataSet schema 
will be extended by this DataSet Extender.

Constraints

C1DataExtender allows you to define row and column level constraints expressed
in code from any programming language supported by .NET Framework.
Constraints code can be defined in design-time and run-time.

Constraints can be defined in the DataSet Extender and in a C1DataView of
C1DataViewSet.
Constraints defined in the DataSet Extender are evaluated
independently of how data is modified - via a C1ViewRow of a C1DataView
or directly through a DataRow of a DataTable. DataSet Extender 
constraints code operates with an ADO.NET DataRow, but not with C1ViewRow.
Constraints defined in C1DataView are evaluated only when changes
are made via a C1DataView row and only for the row in which modifications
occurred. Such constraints operate with C1ViewRow objects.

Constraint expressions are defined by the ConstraintExpression property of 
the following corresponding classes: DataTableExtender
(represents a table in DataSet Extender), DataColumnExtender
(represents a column in DataSet Extender), C1DataView and C1ViewColumn.
A column constraint is evaluated when a column is about to be
changed, and row constraints (defined in DataTableExtender and C1DataView) are
evaluated when the EndEdit method is called for a row.

You can define a constraint with the following properties:
- ConstraintExpression.Expression.Language - defines the name of the .NET Framework 
  language to be used.
- ConstraintExpression.Expression.Text - defines the code of expression,
  written in the specified Language.
- ConstraintExpression.ErrorDescription - defines (optional) a custom message
  for the exception which will be raised if the constraint is violated.

You should treat ConstraintExpression.Expression.Text as a body of a 
static (shared) method with the following definitions:

- for row level constraints:
bool Eval(row);
where row is of type DataRow for DataSet Extender constraint and of type
C1ViewRow for C1DataView constraints.

- for column level constraints:
bool Eval(row, column, newValue);
where the column parameter is of type C1ViewColumn for a constraint defined 
for C1ViewColumn and of type DataColumn for a constraint defined for DataColumn 
in DataSet Extender, the newValue parameter of type represents a new value that is 
being setting to the column, and the row parameter as described before.

Constraints usage is demonstrated in the ConstraintsForm form.
This form contains a viewset (c1DataViewSet1) with a single C1DataView having the 
following definition:
"Select * From [Order Details]"

This viewset is connected to the nwindDataSet1 typed DataSet and references
"NWindDataSetExtender" DataSet Extender, defined as a global project item.
There are two DataGrid(s); the left one is bound to C1DataView from c1DataViewSet1,
and the right is bound to the [Order Details] DataTable from nwindDataSet1 
(i.e., to the underlying DataTable of the view).

Constraints are defined in design-time:
- in DataSet Extender for the [Order Details] DataTable (row level) and for its
  "Quantity" DataColumn
- in the DataView of c1DataViewSet1 - for DataView (row level) and for its
  "Quantity" C1ViewColumn

The general sense of constraints is as follows:
- DataSet Extender
--- 0 < Quantity <= 50 (column level)
--- 0 < UnitPrice * Quantity <= 100 (row level)

- View
--- 0 < Quantity <= 20 (column level)
--- 0 < UnitPrice * Quantity <= 50 (row level)

That is, DataSet Extender and DataView constraints are similar; they only differ 
in upper bounds, and DataView constraints are stronger then DataSet Extender ones.

At the top of the form there is a GroupBox which allows the user to see these four 
constraint definitions by selecting an appropriate radio button. It also allows 
the user to change definition(s) and apply the changes by pressing the "Update Constraint"
button.

Run the application and open the "Constraints" form from the "Forms" menu.
Enter 10 in the Quantity column of the first row of the DateView (left grid), and 
switch to another column (in the same row!). Notice that the value has been changed.
Now enter 30 in the same column and switch to another column. Notice that the value 
reverted back to 10.
The value reverted because the view column constraint has been violated (Quantity <= 20). 
Note that the grid doesn't show column changing exceptions, it just remains the value 
it was.

Now enter 30 in the Quantity column of the first row of the DataTable (right
grid). Notice that the value has been changed successfully. The value changed 
because only the DataSet Extender column constraint (Quantity <= 50) is checked here. 
Now try to enter 60 here and notice that the value reverted back to 30 because the 
DataSet Extender column constraint has been violated.

Now back to the View grid: enter UnitPrice = 20, Quantity = 3, and try to leave
the row – the constraint violation dialog box appears, because the row level constraint
(UnitPrice * Quantity <= 50) has been violated. Cancel changes to the row and try
to enter the same values in DataTable – notice that the values have been applied 
successfully. The value changed because only the DataSet Extender row constraint has 
been evaluated for DataRow.
Now change the Quantity to 10 here and try to leave the row – the DataSet Extender 
row constraint is violated.
