Search This Blog

Wednesday, May 29, 2013

Discussing custom forms and controls (again)

I recently updated the E911 example to 13.1 (it was very smooth, btw - only one change from IComplexPropertyEditor to IComplexViewItem) and refactored it a bit (improved some code, added more code comments and fully rewrote the description).

I think that you may be interested in checking this updated example (I reposted its description below), because it may help you better understand ideas behind XAF as well as how it works and is built internally.

In addition, I recommend you check out the following help articles in the XAF documentation:

eXpressApp Framework > Concepts > UI Construction
eXpressApp Framework > Concepts > UI Construction > UI Element Overview
because they should give you a better understanding of reusable XAF building blocks and see how to customize or replace them.

This example implements the following scenarios when an end-user clicks on a custom item in the navigation control:
- a custom non-XAF form is opened as a result;
- a standard XAF View containing a custom user control is opened as a result.
Both custom form and user controls display persistent data from the XAF application database:

To accomplish this, you can follow the instructions below:

1. Define a base structure of the navigation control in your XAF application, as shown in the E911.Module\Model.DesignedDiffs.xafml file.
You can simply copy and paste the contents of the NavigationItems element into the corresponding file (pre-opened via the text editor) of your platform-agnostic module.
The same customizations can be achieved via the Model Editor visually.

This navigation structure will be further customized in the WinForms and ASP.NET executable projects later.

2. Intercept events of the navigation control to display a custom form when a custom navigation item is clicked.
To do this, implement a WindowController into your platform-agnostic module and handle events of the ShowNavigationItemController class as per the E911.Module\Controllers\ShowCustomFormWindowController.xx file. This controller will be abstract and be overridden in WinForms and ASP.NET modules.

3. Declare a custom ViewItem class that is supposed to host a custom user control in the standard XAF View. To do this, implement a custom ViewItem descendant and related types in the platform-agnostic module as shown in the E911.Module\Editors\CustomUserControlViewItem.xx file.
This ViewItem will also be abstract and platform-agnostic as it will not create platform-dependent controls, and will just provide a common customization code for both platforms. For instance, the OnControlCreated method will be overridden to bind the created control to data. To access persistent data from the database used by an XAF application, the ViewItem will implement the IComplexViewItem interface that consists of a single Setup method, receiving the IObjectSpace and XafApplication objects as parameters.
To unify our data binding code for both platforms, the IXpoSessionAwareControl interface and an auxiliary XpoSessionAwareControlInitializer class are introduced.
The interface provides a single UpdateDataSource method that is implemented by custom forms and user controls to bind them to data received by means of XPO.
You can use a similar mechanism and modify these auxiliary types to pass other custom data into your custom forms and controls.

4. Define a base structure of the standard XAF View with a custom ViewItem as shown in the E911.Module\Model.DesignedDiffs.xafml file.
You can simply copy and paste the contents of the Views element into the corresponding file (pre-opened via the text editor) of your platform-agnostic module.

5. Create custom forms and user controls in WinForms and ASP.NET executable projects analogous with what is shown in the example. The easiest way to do this is to copy the contents of the E911.Win\Controls and E911.Web\Controls folders and then include the necessary files into your solution. Take special note that these custom forms and controls implement the IXpoSessionAwareControl interface to automatically receive persistent data and other parameters when they are created.

6. Implement platform-dependent behavior to open and customize custom forms and controls. To do this, copy the following files into your WinForms and ASP.NET module projects:
E911.Module.Win\Controllers\WinShowCustomFormWindowController.xx - contains an WinForms version of the WindowController, which is inherited from the platform-agnostic
E911.Module.Win\Editors\WinCustomUserControlViewItem.xx - contains an WinForms version of the ViewItem, which is inherited from the platform-agnostic one;
E911.Module.Win\WinModuleEx.xx - contains a registration code for WinForms version of the ViewItem;

E911.Module.Web\Editors\WebCustomUserControlViewItem.xx - contains an ASP.NET version of the ViewItem, which is inherited from the platform-agnostic one;
E911.Module.Web\WebModuleEx.xx - contains a registration code for ASP.NET version of the ViewItem;
E911.Module.Web\Controllers\WebShowCustomFormWindowController.xx - contains an ASP.NET version of the WindowController, which is inherited from the platform-agnostic one;

These platform-dependent versions of the WindowController and ViewItem are required to implement the creation and display of custom forms and controls using the means specific for each platform. They are also designed to provide the capability to be able to set custom forms and control settings via the Model Editor. For that purpose, custom Application Model extensions are implemented for the Navigation Item and View Item model elements.

7. Set the custom forms and controls settings for each platform.
To do this, copy the contents of the E911.Win\Model.xafml and E911.Web\Model.xafml files into the Model.xafml file in the executable WinForms and ASP.NET projects:


That is it.

1. It is also possible to mix the traditional and XAF development approaches (consult our Support Team if you are not sure how to integrate your standard non-XAF solution into XAF), because an XAF application is a regular .NET application built of reusable blocks like View, ViewItem, Property and List Editors, etc. that eventually create and customize platform-dependent controls exactly the same way you do this without XAF. So, using XAF does not mean something absolutely new and allows you to reuse your existing development skills and practices. Of course, it is possible to create your own reusable blocks if the standard ones do not meet your needs. For instance, the example of a custom View class designed to show a custom form can be found on CodeProject here.

2. This solution contains some generic code (e.g., base WindowController and ViewItem) that is mainly required because our XAF application is for both Windows and the Web. You may avoid this generic code and make a simpler implementation if you are developing for only one platform.

3. You can display custom forms not only when interacting with the navigation control, but from any other place. To do this, intercept the events of a required entity, e.g., an XAF Controller, Action or a View. Refer to the product documentation or consult with our Support Team in case of any difficulties.

4. By default controls layout and user customizations are preserved only for built-in XAF ListEditors, because they have special code for that. If you embed a custom user control into XAF, you need to preserve its settings yourself as well, exactly like you would do when implementing this task in the "old way" in a non-XAF application. Refer to the control's documentation to learn more on how to accomplish this task.
Feel free to contact the respective product team if you experience any difficulties customizing this control.

Tuesday, May 7, 2013

Beware of a rehosted Windows Workflow designer bug in .NET 4.5

After integrating the latest Windows Workflow Foundation features into our product we have found an issue with the rehosted designer:

Workflow - XmlException 'sads:DebugSymbol.Symbol' is a duplicate attribute name.' occurs when saving changes in WorkflowDesigner in .NET FW 4.5

It was actually caused by a bug in Microsoft code and was also reported in MSDN forums.
We have reported this bug to Microsoft:

I would ask every one who is facing the same issue to please upvote (see the green arrow at left) the aforementioned MS Connect item and feel free to post workaround solutions there, if any. Thanks for your cooperation in advance!

Starting with version 12.2.9  (or after installing this hot fix), the aforementioned problem should not occur with our product, because we have introduced a static DevExpress.ExpressApp.Workflow.Win.WorkflowDesignerControlBase.CanSetTargetFramework45 property (it's false by default) that disables the problematic features in the rehosted workflow designer.

Monday, May 6, 2013

XAF and several business classes with the same name, but declared within different assemblies

We have implemented a highly requested feature in the next major version and you probably already know what I am talking about by the subject. If not, try to remember if you have implemented classes with common names like Party, Address, Person, Country in your application and then realized that there are already such entities in the default DevExpress.Persistent.BaseImpl library, which conflicted with your own stuff. It was certainly possible to solve these conflicts by either excluding the BaseImpl library from your project or recompiling it, or by renaming your own classes. Now, there should not be such inconveniences at all!

Starting with version 13.1, you can use classes with the same name but different full names.
To implement this functionality, the identifier generation algorithm for several model nodes has been changed: IModelClass, IModelView, MethodSimpleAction, MethodParameterObjectAction.
Use the DevExpress.ExpressApp.Model.NodeGenerators.ModelNodeIdHelper class to get the necessary ID by a type.
For example, to get a LookupListView's model for the DomainObject type, you can execute the following code:
Type targetType = typeof(DomainObject); string viewId = ModelNodeIdHelper.GetLookupListViewId(targetType); IModelView modelView = Application.Model.Views[viewId];

Friday, May 3, 2013

ListView CollectionSource improvements with regard to sorting

I think you will be pleased to know that in the upcoming 13.1 version we have made several refactorings of  the core parts of our framework that allowed us to implement a few features requested by our customers:

Core - Provide sorting capabilities to the CollectionSourceBase class
Core - Support ListView sorting settings from the application model in Server Mode
Core - Pass sorting options from the application model into PropertyCollectionSource

Now, when you add/remove the CollectionSourceBase.Sorting list items, sorting is immediately applied to the inner collection. Do not add several elements one after another. Instead, populate a temporary SortProperty list and then assign it to CollectionSourceBase.Sorting. The example below is taken from the ListView.LoadModelCore method implementation.
List<SortProperty> sorting = new List<SortProperty>(); foreach(IModelSortProperty sortProperty in Model.Sorting) { sorting.Add(new SortProperty(sortProperty.PropertyName, sortProperty.Direction)); } collectionSource.Sorting = sorting;
In this instance, all SortProperty items will be applied simultaneously.

We have also added the CollectionSourceBase.CanApplySorting property. When it is set to true, sorting options specified in the application model are applied. In the CollectionSource descendant, the default value of this property is true. Take special note that in the PropertyCollectionSource descendant the default value is false. This is done to avoid accidental overriding of sorting specified in a collection property getter.

Wednesday, May 1, 2013

Splash Screen - Easy Way

I wanted to let you know about another small usability improvement, which may come in handy for your new XAF apps. This is about displaying custom splash forms in your WinForms application. As you probably know, currently you need to provide your own ISplash interface implementation for that purpose. This is not very difficult, but it could be easier, because there is a ready-to-use component in our WinForms suite that may help you.

Let me quote myself from the Support Center thread to describe the aforementioned improvement further:

"Starting with version 13.1, I have made it easier to use the splash forms powered by the SplashScreenManager component from our XtraEditors library.
Now, you can create a custom splash form via the built-in 'DXperience v1X.X Splash Screen' or 'DXperience v1X.X Loading Panel' item templates and then tell XAF to use it by configuring your WinApplication descendant as follows:

[C#]FeatureCenterWindowsFormsApplication xafApplication = new FeatureCenterWindowsFormsApplication(); xafApplication.SplashScreen = new DevExpress.ExpressApp.Win.Utils.DXSplashScreen(typeof(SplashScreen1));

Alternatively, you can display a static image as a splash. To do this, add a required image (say MySplashImage.png) into your executable project and set its BuildAction to EmbeddedResource. After that, you can use the following code:

[C#]FeatureCenterWindowsFormsApplication xafApplication = new FeatureCenterWindowsFormsApplication(); xafApplication.SplashScreen = new DevExpress.ExpressApp.Win.Utils.DXSplashScreen("MySplashImage.png");

If a parameterless DXSplashScreen constructor is used, then the default wait form (DevExpress.ExpressApp.Win.Utils.DXSplashScreenForm) is displayed.


The DXSplashScreen class can also be used to display a splash image or provide a "live" splash notifying users about current progress.
Refer to the
%Public%\Documents\DXperience 13.1 Demos\eXpressApp Framework\FeatureCenter\CS\\FeatureCenter.Module.Win\Splash\SplashController.cs file for more details on how this can be customized."

Check the following video to see how this works in action:

View on »