After reading the
“What do you need from forms?” Review, I wanted to tell you how in our framework for quickly creating LOB applications of the
eXpressApp Framework “universal, dynamically changing forms” are arranged.

In the first part of my story, I will demonstrate the implementation of dynamic elements using the example of popular tasks of filtering value, controlling visibility and accessibility, as well as controlling data fields on a form of such an unusual business object:
[DomainComponent] public interface ICustomer : IOrganization, IAccount { }
In the beginning was the Word ... or rather a business object!
Out of the box, the framework provides several basic types of forms or “Views”, the purpose of which is largely clear from the name:
• List View - used to represent the list of business objects;
• Detail View - used to provide detailed information about the object;
• Dashboard View - serves to display several different views, i.e. is a container that generally knows nothing about business objects.
And where is the business object? The fact is that eXpressApp Framework can automatically generate these forms based on business object / entity definitions. Technically, the framework first creates metadata representing the skeleton of the future application, and then during its execution uses this metadata to build the final user interface, including navigation systems and commands, CRUD object representations, and much more that is now called the buzzword “UI Scaffolding.
I think enough theory, let's demonstrate the above with a concrete example. First, we will create the ICustomer business entity (for simplicity, instead of classes, I will use interfaces or
Domain Components , as we call them):
[DomainComponent] public interface ICustomer : IOrganization, IAccount { }
which will consist of several components:
[DomainComponent] public interface IAccount { string Email { get; set; } string Password { get; set; } } [DomainComponent] public interface IPerson { string LastName { get; set; } string FirstName { get; set; } DateTime Birthday { get; set; } } [DomainComponent] public interface IOrganization { string Name { get; set; } IList<IPerson> Staff { get; } IPerson Manager { get; set; } }
')
To assess how much the framework makes the life of the developer easier, I’ll give the result obtained after the first launch of the application, which contains only the business entity from the example above:


I want to separately note that largely due to the presence of a metadata layer, our framework can automatically build a user interface for several platforms using the same code base (in general, these are business entities, controllers and commands, field and view editors).
So, in our case, having only one ICustomer business entity, in a few minutes I received multifunctional Windows and Web applications.
We start the heart of "smart" form
To be honest, I really liked the term HUBs (Field-Condition-Value), which was actively used by the author of the
previous article to define the elements of dynamics or rules of form behavior. One of the most popular approaches to advertisements of HUDs is a declarative approach, which implies decorating the attributes of a business object and its members (for sure well familiar to you from
Data Annotations ). In this chapter I will explain how to implement several popular business rules in our framework using a declarative approach.
Filtering field values depending on the business rule
One way to filter field values is to use built-in attributes:
DataSourceProperty and
DataSourceCriteria (
learn more about them from the documentation ). For example, we need the Manager property of our IOrganization to display only records from the Staff collection, and not all records of type IPerson. It's very easy to do this:
[DataSourceProperty("Staff")] IPerson Manager { get; set; }
As you might guess, the main parameter of this attribute is the name of the property containing the list of objects of the desired type. The attribute is smart enough to understand nested properties. So, for example, if we had the Department property with the Staff collection, we could write this:
[DataSourceProperty("Department.Staff")]
If we want to further refine the filter, add a DataSourceCriteriaAttribute with the necessary criteria:
[DataSourceProperty("Staff"),DataSourceCriteria("StartsWith(FirstName, '123')")] IPerson Manager { get; set; }
As a result, we get the following expected result:

Do not forget that all this will also work fine on the Web. If needed, you can also manage these filters through the application's metadata. Of course, you can implement more complex conditions in which the criterion is not specified by a special
object-oriented criteria language , but by program code (for example, in the simplest case, we can declare a private property that will return any list of filtered objects for the DataSourcePropertyAttribute). More examples on the implementation of this scenario in our framework can be
found in the documentation .
Control of field values depending on business rule
Let's make our Manager field mandatory if the Staff collection is not empty. To do this, we use the built-in attribute
RuleRequiredField and set the necessary condition in its TargetCriteria parameter:
[RuleRequiredField(TargetCriteria = "Staff.Count > 0")] IPerson Manager { get; set; }
All this is quite simple, but what if we need to implement something more complicated than a required field? The framework also has an answer to this, since “out of the box” it provides a
couple of dozen of popular data control
rules that are suitable for almost all occasions.
Check it out! For example, we want to make sure that the Email field of our IAccount will be a unique and valid email address. For this, it is enough to add a couple more ready-made attributes:
[RuleRequiredField, RuleUniqueValue] [RuleRegularExpression(@"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[az]{2,4})$")] string Email { get; set; }
The errors are visualized using the built-in module Validation, which is used in the new eXpressApp Framework solution by default:

Whether it is worth mentioning that there is also an easy opportunity to create more complex data control rules in the code, or to customize existing rules / declare new applications in the metadata:

You can learn more about the control capabilities of these forms
from the documentation .
Changes in the appearance, visibility and availability of fields, as well as other controls depending on the business rule
This functionality is provided by the built-in
Conditional Appearance module, which can be easily added to our project by simply dragging and dropping from the Visual Studio Toolbox, while being in the designer of the module (shown in the figure below) or the designer of the entire XAF application:

As a result, a link to the module assembly will be added to the project. After that, you will be able to use the built-in
AppearanceAttribute to customize the style, accessibility, visibility of editors and field headings, as well as other controls, such as the menu options Save, Update, etc.
So, let's begin:
Style change
[Appearance("MarkUnsafePasswordInRed", "Len(Password) < 6", FontColor = "Red")] string Password { get; set; }
This rule will cause the Password field to be red if it has less than six characters:

In a real application, I think you will scare a few with such a highlight, and it is probably better to apply the data control rule with such a regular one again: "^ (? =. * [A-zA-Z]) (? =. * \ D ). {6,} $ ".
You can also change the background color and font settings using the attribute parameters of the same name.
Separately, I wanted to get distracted by “highlighting” the field highlighting, because from experience it often turns out to be useful not only for indicating the state of an object (normal / so-so / bad), but also for hinting the correct sequence of steps on the form (“workflow”).
So, for example, in our internal error tracking system (by the way, written on XAF even a year in 2006), I have the following rule set up, which highlights the green Duplicate ID field, thereby visually indicating to me the right way after setting the Duplicate status to the error report :
<AppearanceRule Id="HighlightDuplicateWhenNonDraft" Criteria="Status.Name == 'Duplicate' AND !Draft" BackColor="192, 255, 192" Context="DetailView" TargetItems="OriginalIssue" Index="9" IsNewNode="True" />
Change availability
[Appearance("ChangeManagerAvailabilityAgainstStaff", "Staff.Count = 0", Enabled = false)] IPerson Manager { get; set; }
This rule will make the Manager field inaccessible if the Staff collection is empty:

Visibility changes
If we slightly change the previous rule, we can control visibility instead of accessibility:
[Appearance("ChangeManagerAvailabilityAgainstStaff", "Staff.Count = 0", Visibility = ViewItemVisibility.Hide)]

The ViewItemVisibility enumeration contains the following values: Hide, Show, ShowEmptySpace. Hide does not leave any “hole” after itself, while ShowEmptySpace will literally leave a “hole” on the form (our users had scenarios when it’s not so bad, well, or just not all users like it when they have something moves and rebuilt). It is important to note that if nothing appears in the container of fields on the form as a result of hiding, then it will hide itself recursively.
All these rules will also work in the List View, whether it is in view or edit mode (see the picture from our demo application):

Again, you can set such rules not only through the attributes in the code, but also through the application metadata:

Change field values depending on business rules
I wanted to give a couple of examples of how to make fields on a form computable. To do this, Domain Components uses
CalculatedAttribute , which accepts an expression to calculate in our criteria language. In this case, if the fields from the expression are stored in the database, the expression will be counted on the server, and not on the client. Here are a couple of examples of such calculated fields:
[Calculated("Concat(FirstName, ' ', LastName)")] string FullName { get; } [Calculated("Invoices[Status == 'Completed'].Sum(Amount)")] decimal SaleAmount { get; }
Something more complex can already be programmed without the use of attributes.
Add fire ...
By default, most HUDs are not recalculated instantly, but when the focus leaves the editor of any changed field, i.e. when the value from the field editor goes directly to the object. Recalculation can also often be caused by some external factors or events, for example, saving, updating, changing the current object on the form, etc. This behavior is acceptable in many cases, since it allows you to avoid unnecessary “noise” on the form, not to mention queries to the server. Nevertheless, there are a number of scenarios where it is simply necessary that our HUBs should be “smarter”. For this, our framework provides a special attribute -
ImmediatePostDataAttribute , which, when applied to a business entity field, causes a value change event instantly, rather than waiting for the focus to leave the editor of this field.
Unfortunately, there have been cases when this attribute has brought dubious benefits. I can't help remembering individuals who had about 80 rules for customizing the appearance of fields on one form, coupled with ImmediatePostDataAttribute (I don’t know, however, whether it is a great love of attributes, or the strangeness of the initial business requirements). We had to sweat a bit so that the performance remained “on the level” and the form did not “die” from such insinuations.
The fact is that when you change the value of one of the fields, you first need to recalculate the whole hundred rules (“brute force” -a approach), and then, at best, change the colors and accessibility of the fields, and at worst repeatedly rearrange the structure of the form. Not much going into the details of our implementation, I would say that we managed to achieve the goal mainly due to the “lazy” creation of field editors, competent caching and changing the state of a form element only if its current state differs from the new one calculated by the rule. Probably, in theory, it would be possible to further improve the performance by eliminating some rules with the help of cunning heuristics, which would parse the criteria of the CCP and apply it only if it really depends on the modified field, ...
To be continued...
In the following parts, I hope to tell you in more detail about the purpose and capabilities of the application's metadata, which determine how our forms will ultimately look and behave. I also think that it will be interesting for the community to learn more about the Domain Components technology (everyone is used to classes, and then some interfaces suddenly appeared incomprehensible), which I used to create business entities. In short, this technology was invented by us for a more flexible and convenient creation of reusable business entity libraries. So far, who are interested, you can see the
article on the Code Project or
documentation in English .