Search This Blog

Thursday, December 6, 2012

Embedding custom controls and forms is easy in XAF

Just wanted to remind you of the subject, because of a recent conversation with a customer about the new DevExpress XtraDashboard product:
http://www.devexpress.com/issue=Q449507
E911

I suggest you take another look at the How to show custom forms and controls in XAF (Example) that shows how to implement it yourself.
Of course, XAF provides a ton of other extensibility points and customization approaches. It is easy to use them once you understand the XAF UI construction principles.

Wednesday, December 5, 2012

Do not miss the "What's New in Help" section for XAF and XPO in 12.2

Knowing about new features is good, but knowing how to use them is priceless.

So, as always, do not forget to check out the What's New in Help section of our What's New document.

 

Tuesday, November 20, 2012

Controlling the size of popup windows on the Web

As you probably know, it is possible to control the size of Web popup windows in code (btw, would not it be nice to control this via the application model as well?) or end-users can perform resizing themselves. Starting with 12.2, end-users will also be able to quickly maximize the popup window using the familiar interface. Let's see the attached video for more details:

MaximizeWebPopupWindows.swf Download this file

In this particular case, all kudos go to our ASP.NET team, because they have improved the ASPxPopupControl component in 12.2 and my work was to just enable a single option by default and ensure that it operates correctly in XAF Web UI. I hope you are looking forward to the 12.2 release!

Monday, November 19, 2012

Getting started with EasyTest (lazy man tutorial)

If you are not using our functional testing framework  - EasyTest yet (if you fall into this category, do not feel bad as you are not alone - our stats (example) show that you are still in the majority:-) ), then this is a good chance to start!

I would like to talk about an internal *AutoTest command that we use to test XAF Code Examples during the initial testing phase internally (before running example-specific tests). This command checks all the navigation items in your app and ensures that list and detail forms can be opened without errors. Not much, but at least it allows you ensure the minimal possible quality of your application before delivering it to the client. Of course, if you wish more, you can choose from dozens of other built-in EasyTest commands or even implement a custom EasyTest command with blackjack and ..., you know:-)

BTW, creating a custom EasyTest command is not that difficult at it appears, expecially taking into account the detailed tutorial and the availability of the full source code of the built-in EasyTest commands:
%ProgramFiles%\DXperience 12.1\Sources\DevExpress.ExpressApp.Modules\DevExpress.ExpressApp.EasyTest.WinAdapter\
%ProgramFiles%\DXperience 12.1\Sources\DevExpress.ExpressApp.Modules\DevExpress.ExpressApp.EasyTest.WebAdapter\
Well, returning to simple things, this is how to use the command I was talking about:
#Timeout 10
#DropDB SimpleProjectManager
#Application SimpleProjectManagerWin
;#Application SimpleProjectManagerWeb
*AutoTest
You can past this stuff (correct app names first, of course!) into the <YourSolutionName>.Module\FunctionalTests\Sample.ets file (it comes with the default XAF solution template) and then right click to choose the Run command in the menu (see this help article for more details).
And here is the result it produces:


View on screencast.com »
Seriously, it is time to start with EasyTest, no more excuses;-)

P.S.
As I mentioned in the beginning, this command is internal and unofficial. We also have not tested it under all scenarios supported by XAF. That said, we may not be able to fix issues with it in a reasonable time frame, so please use it at your own risk, and feel free to modify and test it further to better meet your business requirements.risk. Or better implement normal functional tests for each required screen in your app using the wonderful EasyTest Script Reference.

Some usability improvements to the BO Designer

Some of you will probably be interested in knowing that the BO Designer will have a few usability improvements in version 12.2. I have summarized what I have recently implemented to the designer in this short video:


View on screencast.com »

Hope you like it.

Friday, November 2, 2012

System.Type of DC interfaces vs registered business entities

If you are using Domain Components (DC), you may be already aware of the fact that XAF generates real business entities from registered DC interfaces at runtime. If you have a code that checks types of the current object (e.g., you want to do something only if the current object is of YourBusinessEntityType), you should take this important fact into account, otherwise it may lead to an error, which is quite easy to diagnose, though. A good example of this can be found in this Support Center ticket (which actually forced me to blog about this):

using DevExpress.ExpressApp.DC;
using DevExpress.ExpressApp.Xpo;
...
private void checkType_Execute(object sender, SimpleActionExecuteEventArgs e)
        {
            foreach (object current in View.SelectedObjects)
            {
                /* This is wrong, because real types are generated at runtime based on registered DCs.
if (current.GetType() == typeof(DomainComponent1)) 
                */
                //The next three variants are good to go.
                //if (typeof(DomainComponent1).IsAssignableFrom(current.GetType()))
                //if (current.GetType() == ((IDCEntityStore)XafTypesInfo.PersistentEntityStore).GetGeneratedEntityType(typeof(DomainComponent1)))
                if (current.GetType() == XpoTypesInfoHelper.GetXpoTypeInfoSource().GetGeneratedEntityType(typeof(DomainComponent1)))
                    DoSomethingUseful();
            }
        }
...

As you can see, DC provides you with a method that returns the generated runtime type. Also, the type of the generated entity supports or implements DC interfaces used to form this entity, so you can use the System.Type.IsAssignableFrom method here.
I hope you find this information helpful.

Thursday, November 1, 2012

It's not a bug, it's a feature!

Are you aware of this "hidden" feature of the XAF Tabbed MDI interface?:-)
If not, it is never late to learn! In addition, I suggest you review one more help article that describe other hidden goodies. I hope you find this information helpful.


View on screencast.com »

Tuesday, October 30, 2012

New (official) FaceBook and Twitter pages for XAF & XPO

I already blogged in the past about our unofficial groups in social networks, but decided to create official accounts, because they provide a way more capabilities for managing information and communicating with our users, plus have a fixed url that is easy to remember and recognize.
So, feel free to like us on FaceBook and follow on Twitter. As always, we look forward to hearing from you, sharing news and answering your questions about our tools.
BTW, there are also various changes coming to the XAF landing page in the coming weeks (probably once 12.2 is out)...So, let see what develop!

Thursday, October 25, 2012

XAF Training in Germany (again!) - November 19-23

It seems that Oliver will soon run XAF trainings every month;-)

From http://www.oliversturm.com/expert-xaf.html:

"This class provides a complete in-depth look at the DevExpress eXpressApp Framework. You'll start with the basics of creating a data layer with XPO and/or Domain Components, and learn how to develop within the XAF infrastructure for both WinForms and ASP.NET WebForms platforms. All the modules supplied with XAF will be introduced and explained in enough detail to help you start developing your own XAF based applications very quickly! Questions are encouraged and will be answered as much as time permits!"

This is what Marc Greiner said about this training:

"I highly recommend this training to any developer involved in business application development. I had the opportunity to attend this training last june and I believe that like me, all of the participants had a lot of fun at Bad Ems, discovering all that can be done in minutes with XAF. Many of the participants were XAF new comers, but there were also several well experienced ones, some of them with several years of XAF experience, having built really stunning web and Winform XAF apps. From the feedback that they gave, they highly appreciated the material and all could learn lots of things.

Big thanks again for your great pedagogic skills, your patience and your will to share the knowledge, and also to John for organizing the event and the evening outings!"

Making XAF Conditional Appearance more user-friendly

Take special note of the following video:


View on screencast.com »

It shows how to customize the Conditional Appearance module to allow your end-users to easily create Appearance rules directly from Views and not by using the Model Editor tool.

Refer to the http://www.devexpress.com/issue=Q381881 ticket for the source code of this extension. In my opinion it is a very smart piece of work and your end-users will like it.

Working with links to files instead of storing their contents in the database

If you read my previous post about a reusable File System Data module I created some time ago, you might remember that it included a custom IFileData implementation (this simple interface/contract is used by our buit-in File Attachments module that provides a generic solution for working with files in both desktop and Web apps). My solution was designed to work with links to real files instead of storing their contents in the database. Before today, it had a small issue that does not allowed you to keep changes to files opened from these links (e.g., you opened a *.docx file in Word, edited it and then saved), because a temporary copy of the file was created by default. I discovered this issue with the help of a customer (thanks, Will!) and updated the E965 example accordingly. The fix was in handling the CustomOpenFileWithDefaultProgram event and using the Process.Start method. Now, everything operates as expected:


View on screencast.com »

This is just yet another example on how our eXpressApp Framework is flexible as well as on how the community feedback is important to run our mutual progress.

XPO Session vs XAF ObjectSpace (for starters only)

I think that the explanation in the http://www.devexpress.com/issue=Q415894 ticket willl be helpful for people who get started with XAF:

"In XPO, if you use Session and want to save your changes, you should either call the Save method of your persistent object or use the CommitTransaction method of the Session class.
If you use UnitOfWork (it is a descendant of Session), then you should call its CommitChanges or CommitTransaction methods. You can also use the Save method to mark your persistent object as modified.
In XAF, there is a similar entity called ObjectSpace. It uses UnitOfWork internally when XPO is used for data access. To save data in an ObjectSpace, use its CommitChanges method.
To create a new object, use the CreateObject method of the ObjectSpace class. Of course, you can access the underlying XPO session via ((XPObjectSpace)theObjectSpace).Session .
So, these are the same scenarios, just implemented differently.
I hope this clarifies things for you."

These concepts are now more detailed in the XAF documentation at eXpressApp Framework > Concepts > Data Manipulation and Business Logic

What are the equivalents of traditional buttons, menu, toolbar and navigation items in XAF?

I believe that the explanation provided in the http://www.devexpress.com/issue=Q416185 ticket will greatly help understand the concept of commands - Actions in XAF, especially for newbies.

«Умные» формы eXpressApp Framework (XAF). Часть 2 — Метамодель UI приложения

«Умные» формы eXpressApp Framework (XAF). Часть 1

Object reference not set to an instance of an object...

We often get reports from our customers where they send us only a screenshot that just contains the subject. Well, we can sometimes call the Force, contact Jon Skeet to debug such screenshots or rather call our local seers to decrypt this message and find the original cause of this error, but unfortunately, it does not work in the majority of cases.





















Instead, you, as a developer can simply enable CLR exceptions in your Visual Studio and debug your application to see the full error details (message, callstack, inner exception, etc.). Specially for XAF apps we also generate the eXpressAppFramework.log file, which you can also inspect yourself and see a detailed error report.

Apparently, the information taken from the log file (or got during debugging) is a way more useful for sending to us. Also, often you can just look at the error details and figure out solution problem yourself, especially if there are no DevExpress routines in the call stack.
I hope this information will help you save time on communicating with our Support Team, because sometimes such errors may occur in production...


Creating persistent objects in a few keystrokes

Just wanted to share a link to the list of wonderful CodeRush shortcuts for XPO because not everyone is aware of them and they are either wasting their time by typing persistent properties in the recommended style manually or giving up on the recommended style and violating XPO Best practices.

Have I also mentioned that you will be soon able to forget about typing persistent properties at all, because of very useful additions to XAF and XPO in 2012?

See also:
CodeRush Shortcuts and Templates - Quick Start Sheets
DevExpress Webinars - Using CodeRush in XAF
Creating a CodeRush Code Template for a XAF property

 

Discussing a typical scenario of business model design

I recently had an interesting support conversation with a customer at
http://www.devexpress.com/issue=Q382370. While the name of this ticket is a bit misleading (customer hoped to implement it this way in the beginning), the whole discussion is very helpful (especially for beginners with XAF and XPO), as it describes a typical business case and several solutions of it as well as their pros and cons.
Well, imagine you need to provide a comments functionality to some of your business objects, but not to all. These objects will hold a collection of Comment objects that,at once, may provide some specific properties against a type, e.g. UserComment, EventComment, etc. The most easy and efficient solution is to use inheritance here. That is creating a base Comment class and establishing a One-To-Many relationship with a base class for comments. Business objects descended from this class will automatically get the comments functionality we wanted.
While that is a good solution, it may, under certain scenarios, be inappropriate to descend from a common base class. For instance, it may be required to inject many additional functionalities similar to comments. That is, when multiple inheritance (not allowed for classes in C#) would come in handy. Good news for us that we have the great Domain Components (DC) technology! Simply said, it is ideal for these sorts of scenarios. For instance, I could easily compose the following business entity:
[DomainComponent]
public interface ICRMProduct: IGenericProduct, IComments, IWhatever
and thus emulate a multiple inheritance from these interfaces. Check the XCRM demo shipped with eXpressApp Framework (XAF) for more real-world examples.
If you are not using XAF and cannot use DC (btw, you can use DC in non-XAF applications too!), there is also another good solution for you. The idea is to compose your business object from required parts providing certain functionality using the aggregation/composition principle. Aggregation and composition in programming are very similar (the latter is a special case of the first, and I personally sometimes mix them, probably due to the misleading name of XPO's AggregatedAttribute:-)) as they differ only by the fact that in composition the part cannot live without the whole object containing it, plus, it is destroyed with the whole object as well. You can find more strict definitions of these design patterns in smart books and on the Web and this is not the subject of this short post. Well, technically, you could create a Comments Container object holding a collection of comments and then add a reference to this Comments Container to required business objects. Since a working example code is worth a thousand words, refer to the sample project I attached at the end of the Support Center ticket I mentioned in the beginning. It contains XPO classes that correctly implement that relationship. Finally, I want to mention that we use the same principle in Domain Components (DC) when emulating multiple inheritance internally.
I hope this short note was helpful and you learned a bit from it. You can learn more on business model design in XAF and XPO from the documentation.
Till next tip!
Screenshot

"Ideal" development workflow for adding a custom functionality in XAF

In this "short" post I'd like to demonstrate the subject by example of implementing the following suggestion: SystemModules.Link - Exclude objects that are already linked to an object from the Link ListView. Good for our learning purposes, this feature request is representative and small enough at the same time. Technically, we will improve the standard LinkUnlinkController class behavior. Let's start doing real coding!

Implementation/Refactoring

If you look at the current 11.2 code of the controller, you will notice that it is quite difficult to provide your own link View for it:

private void linkAction_OnCustomizePopupWindow(Object sender, CustomizePopupWindowParamsEventArgs args) {
linkListView = (ListView)CreateLinkView();
args.View = linkListView;
args.DialogController = Application.CreateController<LinkDialogController>();
((LinkDialogController)args.DialogController).Initialize(lookUpEditorHelper,
lookUpEditorHelper.CanFilterDataSource(linkListView.CollectionSource, MasterObject));
}

Note that according to this code the link View must be always a ListView. Let's fix this by adding more flexibility as follows:

private void linkAction_OnCustomizePopupWindow(Object sender, CustomizePopupWindowParamsEventArgs args) {
View linkView = CreateLinkView();
args.View = linkView;
linkListView = linkView as ListView;
if (linkListView != null && lookUpEditorHelper != null) {
LinkDialogController dialogController = Application.CreateController<LinkDialogController>();
dialogController.Initialize(lookUpEditorHelper, lookUpEditorHelper.CanFilterDataSource(linkListView.CollectionSource, MasterObject));
args.DialogController = dialogController;
}
}

As you see, I removed the direct cast to ListView and also slightly refactored the rest code. Apparently, now the linkListView variable must be checked on null before using.

If you look at the current CreateLinkView method code, you will notice that it creates the link View of the ListView type only. Since we want to provide the capability to create the link View of an arbitrary View type (e.g., check out this ticket for a real business scenario), we can refactor the method by introducing the event and also moving the default ListView creation code into a separate method:

protected virtual View CreateLinkView() {
CustomCreateLinkViewEventArgs customArgs = new CustomCreateLinkViewEventArgs(View);
OnCustomCreateLinkView(customArgs);
if (!customArgs.Handled || customArgs.LinkView == null)
return CreateLinkViewCore();
return customArgs.LinkView;
}
protected virtual void OnCustomCreateLinkView(CustomCreateLinkViewEventArgs customArgs) {
if (CustomCreateLinkView != null)
CustomCreateLinkView(this, customArgs);
}

The new event and its arguments declarations will look as follows:

public event EventHandler<CustomCreateLinkViewEventArgs> CustomCreateLinkView;

public class CustomCreateLinkViewEventArgs : HandledEventArgs {
private ListView sourceViewCore;
public CustomCreateLinkViewEventArgs(ListView sourceView) {
sourceViewCore = sourceView;
}
public ListView SourceView { get { return sourceViewCore; } }
public View LinkView { get; set; }
}


To complete support of custom link Views, we need to slightly refactor the Link method code, because it currently takes selected objects from the default ListView only:

protected virtual void Link(PopupWindowShowActionExecuteEventArgs args) {
LinkObjects(args.PopupWindow.View.SelectedObjects);
}

My refactored code is shown below:

protected virtual void Link(PopupWindowShowActionExecuteEventArgs args) {
QueryLinkObjectsEventArgs customArgs = new QueryLinkObjectsEventArgs(args.PopupWindow);
OnQueryLinkObjects(customArgs);
IList linkObjects = customArgs.LinkObjects;
if (!customArgs.Handled && linkListView != null)
linkObjects = linkListView.SelectedObjects;
LinkObjects(linkObjects);
}
protected virtual void OnQueryLinkObjects(QueryLinkObjectsEventArgs customArgs) {
if (QueryLinkObjects != null)
QueryLinkObjects(this, customArgs);
}

Here the QueryLinkObjects event and its arguments look as follows:

public event EventHandler<QueryLinkObjectsEventArgs> QueryLinkObjects;
public class QueryLinkObjectsEventArgs : HandledEventArgs {
private Window linkWindowCore;
public QueryLinkObjectsEventArgs(Window linkWindow) {
linkWindowCore = linkWindow;
}
public Window LinkWindow {
get { return linkWindowCore; }
}
public IList LinkObjects { get; set; }
}

So, a guy who wants to implement a custom link View will have to handle these two events, provide their respective LinkView and LinkObjects parameters and do not forget to set the important Handled parameter to True.

We are now done with the first part and are ready to implement the main functionality - excluding already linked objects from the link View. Apparently, we need to modify the newly introduced CreateLinkViewCore method for that purpose. Since we are creating a ListView in this method, we need to add a criterion to its CollectionSource, as described in this help article from our docs:

if(MasterObject != null) {
CriteriaOperator associatedCollectionCriteria =
ObjectSpace.GetAssociatedCollectionCriteria(MasterObject, ((PropertyCollectionSource)View.CollectionSource).MemberInfo);
result.CollectionSource.Criteria[CriteriaKeyForLinkView] = new NotOperator(associatedCollectionCriteria);
}

As you see, I just took association criteria and then inverted it.

It seems that we are done with our main implementation and now it is time to check whether this code ever works. The easiest way to do this is to run our MainDemo application and test the improved Link functionality with Contact and its Tasks list:

Linkunlinktest

Thanks God, my code worked like a sharm in both Windows Forms and ASP.NET applications. Some of you may think that we are now fully done, but in fact we are only in the middle of our path... Why? The answer is simple - we must ensure that the code we just wrote will work fine in future versions of our product, preferably without any input from God.

Unit Testing

The best way to ensure this is to cover the implemented functionality with unit and functional tests. I strongly believe that it is simply damn wrong and plain stupid not to do this (no mercy) and as a result manually test this functionality each and every release in the future. Writing tests will also make you a better developer.

Fortunately for me (and for you too!), the developers of our Core team already had some unit tests for LinkUnlinkController in the LinkUnlinkControllerTests class and my task is to just add a few test methods to cover my code. Practically, that means that I do not need to create a required testing infrustructure from scratch and can just reuse existing test objects and probably grab some example code blocks from other tests. Another good thing is that I can make use of existing base test classes and mocks like BaseXafTest, TestApplication, etc. that provide useful methods and allow me to write platform-independent tests. Let's demonstrate this by an example.

First, I will extend the LinkUnlinkController descendant used only for tests with a couple of overridden methods to provide counters for my events - I want to ensure that they actually fire when needed and their arguments are taken into account:

public class LinkUnlinkControllerForTests : LinkUnlinkController {
public int CustomCreateLinkViewEventCounter = 0;
public int QueryLinkObjectsEventCounter = 0;
public LinkUnlinkControllerForTests() : base() { }
protected override void OnCustomCreateLinkView(CustomCreateLinkViewEventArgs customArgs) {
base.OnCustomCreateLinkView(customArgs);
CustomCreateLinkViewEventCounter++;
}
protected override void OnQueryLinkObjects(QueryLinkObjectsEventArgs customArgs) {
base.OnQueryLinkObjects(customArgs);
QueryLinkObjectsEventCounter++;
}
}

This is a very common practice, especially when we need to hook up into some protected members or disable/mock certain functionality for tests only. To give you one more example, the TestApplication class I mentioned earlier is a descendant of XafApplication that overrides its methods for tests, e.g. provides platform-agnostic TestListEditor and TestLayoutManager instead of real platform-dependent classes.

Now we are ready to write our first test method for the functionality implemented earlier:

[Test]
public void TestCustomCreateLinkViewEvent() {
RegisterTypesForModel(typeof(Incident), typeof(Message));
CollectionSourceBase linkedMessagesDS = CreateCollectionSource(incident, "Messages");
TestApplication application = new TestApplication(this, modelApplication);
LinkUnlinkControllerForTests controller = new LinkUnlinkControllerForTests();
controller.Application = application;
ListView sourceListView = application.CreateListView(GetListView<Message>(), linkedMessagesDS, false);
sourceListView.CreateControls();
controller.SetView(sourceListView);
Assert.AreEqual(controller.LinkAction.GetPopupWindowParams().View.GetType(), typeof(ListView));
Assert.AreEqual(1, controller.CustomCreateLinkViewEventCounter);
bool handled = false;
EventHandler<CustomCreateLinkViewEventArgs> customCreateLinkViewEventHandler = delegate(object sender, CustomCreateLinkViewEventArgs args) {
Assert.AreEqual(args.SourceView, sourceListView);
args.LinkView = new DashboardView(ObjectSpace, application, false);
args.Handled = handled;
};
controller.CustomCreateLinkView += customCreateLinkViewEventHandler;
Assert.AreEqual(controller.LinkAction.GetPopupWindowParams().View.GetType(), typeof(ListView));
Assert.AreEqual(2, controller.CustomCreateLinkViewEventCounter);
handled = true;
Assert.AreEqual(controller.LinkAction.GetPopupWindowParams().View.GetType(), typeof(DashboardView));
Assert.AreEqual(3, controller.CustomCreateLinkViewEventCounter);
controller.CustomCreateLinkView -= customCreateLinkViewEventHandler;
}

It's a bit too long, but I need to test all possible combinations.

The next two test methods will be a way more complex and longer:

[Test]
public void TestQueryLinkObjectsEventAndExcludeLinkedObjectsNoServerMode() {
TestQueryLinkObjectsEventAndExcludeLinkedObjectsCore(false);
}
[Test]
public void TestQueryLinkObjectsEventAndExcludeLinkedObjectsServerMode() {
TestQueryLinkObjectsEventAndExcludeLinkedObjectsCore(true);
}

private void TestQueryLinkObjectsEventAndExcludeLinkedObjectsCore(bool useServerMode) {
RegisterTypesForModel(typeof(Incident), typeof(Message));
CollectionSourceBase linkedMessagesDS = CreateCollectionSource(incident, "Messages");
TestApplication application = new TestApplication(this, modelApplication);
application.Model.Views.DefaultListEditor = typeof(TestListEditor);
application.Model.Options.UseServerMode = useServerMode;
LinkUnlinkControllerForTests controller = new LinkUnlinkControllerForTests();
controller.Application = application;
ListView sourceListView = application.CreateListView(GetListView<Message>(), linkedMessagesDS, false);
sourceListView.CreateControls();
controller.SetView(sourceListView);
CustomizePopupWindowParamsEventArgs windowParams = controller.LinkAction.GetPopupWindowParams();
Assert.AreEqual(1, linkedMessagesDS.GetCount());
Assert.AreEqual(windowParams.View.GetType(), typeof(ListView));
Assert.AreEqual(0, controller.QueryLinkObjectsEventCounter);
bool handled = false;
Window linkWindow = new Window(application, "", null, false, true);
EventHandler<QueryLinkObjectsEventArgs> queryLinkObjectsEventHandler = delegate(object sender, QueryLinkObjectsEventArgs args) {
args.LinkObjects = new object[0];
args.Handled = handled;
Assert.AreEqual(linkWindow, args.LinkWindow);
Assert.AreEqual(windowParams.View, args.LinkWindow.View);
};
controller.QueryLinkObjects += queryLinkObjectsEventHandler;
ListView linkListView = ((ListView)windowParams.View);
CollectionSourceBase messagesToLinkDS = linkListView.CollectionSource;
Assert.IsTrue(messagesToLinkDS.Criteria.ContainsKey(LinkUnlinkController.CriteriaKeyForLinkView));
Assert.AreEqual(0, messagesToLinkDS.GetCount());
Message messageToLink = new Message(ObjectSpace.Session);
ObjectSpace.CommitChanges();
messagesToLinkDS.Reload();
Assert.AreEqual(1, messagesToLinkDS.GetCount());
linkWindow.SetView(linkListView);
linkListView.CreateControls();
linkListView.Editor.FocusedObject = messageToLink;
controller.LinkAction.DoExecute(linkWindow);
Assert.AreEqual(1, controller.QueryLinkObjectsEventCounter);
ObjectSpace.CommitChanges();
incident.Reload();
Assert.AreEqual(2, incident.Messages.Count);
handled = true;
messageToLink = new Message(ObjectSpace.Session);
ObjectSpace.CommitChanges();
linkListView.Editor.FocusedObject = messageToLink;
controller.LinkAction.DoExecute(linkWindow);
Assert.AreEqual(2, controller.QueryLinkObjectsEventCounter);
ObjectSpace.CommitChanges();
incident.Reload();
Assert.AreEqual(2, incident.Messages.Count);
controller.QueryLinkObjects -= queryLinkObjectsEventHandler;
}

However, these methods fully test the entire link functionality from the beginning to end. It is also done under both server-side and client binding modes.

Did I mention that writing good unit tests once and for all will help you avoid problems and PITA in the future?

Functional Testing

Finally, to ensure that everything will operate as expected under real circumstances, I will add functional tests. As you probably know, these kind of tests is powered by our EasyTest functional testing framework. With EasyTest I can ensure the correctness of both Windows Forms and ASP.NET applications using a single test script:

#Aplication AppStudioTestWeb
#Application AppStudioTestWin

*Action Navigation(Incident)
*ProcessRecord
Описание = Test Incident

*Action Versions

*Action Versions.Link
*CheckTable
Columns = Name
Row = 1.0
Row = 2.0
*ProcessRecord
Name = 2.0

*CheckTable Versions
Columns = Name
Row = 2.0

*Action Versions.Link
*CheckTable
Columns = Name
Row = 1.0
*Action Cancel

*SelectRecords Versions
Columns = Name
Row = 2.0
*Action Versions.Unlink

*HandleDialog
Respond = Yes

!ProcessRecord Versions
Name = 2.0

That is it. Now I am sure that the code I added will not be broken one day. Or at least I will know if that happens from failed tests.

I hope you find this information interesting as the process I described above will help you ensure a better quality of your XAF applications. I also want to explicitly mention that this process is the same in our team when developing features. The only thing I did not mention is that we also do a manual testing of some scenarios before releasing a new product version.

Forgot to say, that the feature I described here will be available in XAF 12.1. I also hope you also liked this small addition to the framework.

See Also
Best practices of creating reusable XAF modules by example of a View Variants module extension
eXpressApp Framework > Task-Based Help > Testing

How to disable certain actions for a new record in XAF

 

 

You may find this post helpful: http://www.devexpress.com/issue=Q399803 as it describes a quite common business requirement:

"1. Check whether the key property is assigned or not (you can check this either in the TargetObjectsCriteria property for your Action or in an Appearance rule).
It will suffice your needs if you do not use the BaseObject.OidInitializationMode.AfterConstruction.
2. Use the IsNewObject method provided by the Session/ObjectSpace types. For instance:
[C#]
public bool IsSavedObject { get {return !Session.IsNewObject(this); } }"

For instance, in the standard DeleteObjectsViewController we use the second approach:

deleteAction.Enabled.SetItemValue("IsCurrentObjectNew",
!View.ObjectSpace.IsNewObject(((DetailView)View).CurrentObject) || (View.ObjectSpace is NestedObjectSpace)); 

 

About "unusable nodes" in XAF Application Model

The problems with unusable nodes may normally occur only if model elements are not defined in the application model. For instance, you might move Web differences (one layer) to a WinForms or platform-agnostic module project (another layer with a different schema that knows nothing about Web). Or you might remove certain model elements from the application model yourself. More information on this can be found at Why application model differences can be removed into the UnusableNodes.xml file

Normally, these problems should not occur at all if you use the Model Editor and Merge Model tools and do not edit the XAFML files manually.
You can learn more about means of detecting unusable nodes at http://documentation.devexpress.com/#Xaf/CustomDocument3327

So, these are three simple steps you can follow to avoid any problems:
1. Use Model Editor and Merge Model tools to perform operations with the application model;
2. Do not edit XAFML files manually unless you are absolutely certain of what you are doing;
3. Verify that XAFML files do not contain references to removed custom model elements extensions

If this information does not help you to fix unusable nodes in your model, you are welcome to provide us with the XAFML file (or rather a small sample project) before unusable nodes appear + exact steps that cause it + the XAFML file after executing these steps. We will be glad to help you resolve this.

Writing platform-agnostic code to display a confirmation dialog for both Win and the Web

I have just posted a small sample project to the suggestion discussed in the http://www.devexpress.com/issue=S19008 ticket:

"I want to close this ticket with a solution that was successfully used by many of our users through these years.
You can write a platform-agnostic code for displaying a confirmation dialog by using PopupWindowShowAction that will display a DetailView of a non-persistent object or even a DashboardView with a text, image, etc.

It is easy to implement and, taking into account ASPxPopupControl integration in 12.1, this looks like a good solution that fully meets the original requirement. Attached is a small sample project + a screenshow showing how it looks in Win and Web UI."

Screenshot

Specially for two guys from DevCon'12 with whom we chatted a lot about XAF

Here you go:

public abstract class ObjectViewController<ViewType, ObjectType> : 
        ViewController<ViewType> where ViewType : ObjectView {
        public ObjectViewController()
            : base() {
            this.TargetObjectType = typeof(ObjectType);
        }
        public ObjectType ViewCurrentObject {
            get { return (ObjectType)View.CurrentObject; }
        }
    }

 

For people who do not quite understand what is going on, this is a new short way of declaring a ViewController in code to target it for a certain business object and View type at the same time.

So, starting with 12.1, you can write the following code:

    public class MyCoolViewController : ObjectViewController<DetailView, Contact> {
        protected override void OnActivated() {
            base.OnActivated();
            Contact theContact = ViewCurrentObject;
        }
    }

Enjoy! And generics rock!

How to manage users (register a new user, restore a password, etc.) from the logon form

I have just finished refactoring a cool example, which I originally posted to this forum thread two years ago.

You can download the updated version from here. Here is also a small video that shows the implemented functionality in action:


View on screencast.com »

If you decide to use this module in your solution, do not forget to integrate functional tests (E4037.ets) as well.

I hope you find it helpful.

Declaring a base logic class for Domain Components (DC)

Today I came across a very smart piece of work: Domain Logic Base Class => Good Practice?, and I can not share it with you.

This solution enables you to save time by using Intellisense instead of typing logic methods names manually. Probably, this is also where CodeRush may help, but a common base class is still a brilliant solution, which I remember we used in the far far past (notice EntityService there).

What do you think?

Passing XafApplication, ObjectSpace and other data into custom non-XAF forms

Today I came across this Support Center ticket and it forced me to blog about possible solutions to this task, because it is quite common and may be interesting for others.

First, let me stop for a while and describe when a custom form may want to use this information. Imagine that you have a data-aware custom form (e.g., a grid that displays persons) and you want to populate it with data from your database. In order to do this, and if you use the traditional development approach and XPO as a data layer, you will most likely configure your XPO data layer and then create a Session to access your data. When using XAF, this work already done for you by the framework (you just need to specify a required connection string via the designer or in configuration file). So, you do not need to mess with the same connection string in your custom form, but rather use a ready-to-use Session object that already points to the main application database. You can get the XPO's Session object via the XPObjectSpace.Session property. People who are not familiar with XAF should know that ObjectSpace is a ORM-independent entity, data context that provides access to data in XAF applications. There are different implementations of ObjectSpace for different ORMs, e.g. XPObjectSpace  for XPO and EFObjectSpace for EF.

Of course, in addition to quering data, you may also use other XafApplication services in your custom forms. For instance, read some settings from the Application Model - a universal metadata and settings store for XAF applications.

Now, let's talk about passing this data into custom forms.

When implementing your custom form class, you should supply all the external information for its normal operation. Technically, you can do this by introducing special public properties (that will hold required data) and assign them after the form creation. A better and more natural solution would be introducing new constructors with required parameters for your form.

To display a custom form in XAF, you can use a Controller with an Action, e.g. as described here. From this Controller you always have access to the XafApplication object via the Application property. So, you can pass it as a parameter into your custom form. This solution is demonstrated in this Code Central example.

But what to do, if you do not use controllers to invoke custom forms/user controls? How to access XafApplication in this case?

It all depends on your scenario. For instance, if you implement a custom PropertyEditor/ViewItem or ListEditor, then you can implement the IComplexPropertyEditor/IComplexListEditor interfaces by your custom classes. As a result, the Setup method provided as part of these interfaces will be automatically called by XAF and you wil receive required parameters (e.g., ObjectSpace, XafApplication) into your classes for further reuse.

If your scenario is different, you can implement a static helper class that will hold XafApplication (you will have to first initialize it in the Main method, though) so that you can access it from anywhere in your code. It is a bit naive, but is also a good  and easy to implement solution.

BTW, I started to think about introducing a static Instance property (like in XAF Web UI) into the WinApplication class, so that you can easily access it from anywhere using the following code:

WinApplication.Instance

I think that it may simplify lives of many not so experienced XAF devs (so that they are not forced to implement sophisticated solutions like the ones I described earlier). What do you think?;-)

Another DBUpdater improvement - Custom user notifications during the database update

We continue flushing our features backlog, and this time I would like to tell you about a small DBUpdater usability improvement:

From http://www.devexpress.com/issue=S136591:

"I have implemented this suggestion in 12.2. Starting with this version you can call the static DevExpress.ExpressApp.Utils.ApplicationStatusUpdater.Notify method from your ModuleUpdater class to provide additional information about the database update, e.g.:
 
 ApplicationStatusUpdater.Notify("CreateReports", "Creating reports in the database...");

 You can also handle the ApplicationStatusUpdater.UpdateStatus event in your code to be notified of any status updates or to correct messages accompanying these status updates.  To demonstrate this further, I have attached a screenshot of the DBUpdater run output:"

S136591

Another small improvement - Custom sorting for lookups with LookupEditorMode=AllItems on the Web

Before I go on the weekend, here is another good piece of news for XAFers, this time regarding Web lookups.

From http://www.devexpress.com/issue=B150005:

"I have implemented this suggestion in 12.2. Starting with this version, sorting options set for the lookup ListView in the application model under the Sorting node will be implemented when generating items for the drop down.

 

Screenshot

If no sorting is specified, then items will be sorted alphabetically by the display value, as in previous versions."

Hope you liked this news!

Domain Components (DC) + New Security System = LOVE

If you are using DC, you are probably aware of the feature request we have regarding the new security system. We have implemented it in 12.2 and created a new demo for you:

"Starting from version 12.2, we will provide a DC-based demo that works with the new security system. For your convenience, both XPO and DC-based versions will be available as part of our SecurityDemo solution."

Note that as indicated and discussed with our customers in this thread, we did not consider scenarios with assigning permissions to shared parts, but rather to real entities only. This decision was made because it appears to be more logical as end-users can operate with registered entities only, and not with individual domain components that stay behind the scenes. In addition, this significantly decreases the complexity of the entire system and thus makes it more stable.

A small usability improvement - Tooltips for editors, column headers, layout groups, etc.

I have added one more feature in 12.2 that should certainly improve usability of your applications:

Screenshot

From http://www.devexpress.com/issue=S30920:

"Starting with this version, you can specify a new ToolTip property at the Class member, ListView column, DetailView item, navigation & layout group and item levels in the application model. Note that once set for a class member, the tooltip value is automatically calculated for below layers such as ListView column, DetailView item, layout group and item, unless the user has already set a tooltip at these levels.
For more convenience, you can use the new ToolTipAttribute to set tooltip options in the code of your business class.
Take special note that additional tooltip settings such as ToolTipTitle and ToolTipIconType are available for WinForms applications only."

Another DBUpdater improvement - Passing a custom connection string from command line

From S132999:

"I have implemented this suggestion in 12.2. Starting with this version you can use an optional
'-connectionString' parameter followed with a custom connection string that will be used by the DBUpdater instead of the ones specific in the application configuration file or application itself.

Here is an example of how to use the new parameter:

D:\2012.2\XAF\Demos\CS\MainDemo\MainDemo.Win\bin\Debug>DBUpdater.v12.2.exe MainD
emo.Win.exe.config -connectionString "Integrated Security=SSPI;Pooling=false;Dat
a Source=.\SQLEXPRESS;Initial Catalog=Test""

A minor improvement to the ORM Data Model wizard - Defining readonly persistent properties

Our XPO guys again rock, never stoping the XPO visual designer tuning. This time they have added a support for readonly persistent properties:

"We have implemented your request in 12.2. I should explicitly mention that readonly properties generated by the wizard will follow the pattern shown in this help article.
Take special note that it will be also impossible to make delayed properties readonly via the designer."

Diagnosing of security system permissions has been improved

I think that many of you used our new security system and migth receive the following message in the past: "Saving an object is prohibited by validation rules". Not very descriptive!

Starting with the next maintenance update this message will be more helpful:

<data name="Security_SavingAnObjectWasProhibitedByTheRulesOfVal" xml:space="preserve">
<value>Saving the '{0}' object is prohibited by validation rules.</value>
</data>
<data name="Security_SavingAnObjectWasProhibitedByTheRulesWProp" xml:space="preserve">
<value>Saving the '{0}.{1}' property is prohibited by validation rules.</value>
</data>
<data name="Security_DeletingAnObjectWasProhibitedByTheRules" xml:space="preserve">
<value>Deleting the '{0}' object is prohibited by validation rules.</value>
</data>

In one turn, the XPO code (thanks Slava!) will now pass corresponding object type and property name information available in this context. This will save XAF and XPO developers a lot of time on checking their permissions. Hope you liked this small, but a very important improvement.

A minor improvement to the Model Editor tool usability when configuring sorting

Today I also came accross the PropertyEditors.Lookup.Web - Take into account the Sorting settings of the ListView node and do not always sort by the display name when a regular drop down editor is displayed request (will probably work on it soon) and noticed that a convenient field picker is missing in the Model Editor when configuring sorting. I have fixed this in 12.2.

Screenshot

Please reports such cases via the Support Center (www.devexpress.com/sc/) if there are other such places. Thank you in advance!

Meet the new DataSourceCriteriaProperty attribute - another addition to the unlimited XAF filtering capabilities

Another example where you asked, and we have listened and implemented.

From Core - Make it possible to specify a dynamic criteria for lookups in code of business objects (similar to DataSourcePropertyAttribute):

"We have implemented a new DataSourceCriteriaPropertyAttribute attribute in 12.2. You can apply it to collection and reference properties (of course, you can also set it for the class member, ListView column or DetailView item in the application model) to filter them, similar to other standard DataSourceProperty and DataSourceCriteria attributes that work in this scenario.
The only difference is that the constructor of this attribute accepts a string name of the property of the CriteriaOperator type. This capability allows you to *dynamically* perform custom filtering of almost any complexity, and that works in the server mode (which is not always possible when DataSourcePropertyAttribute is used)."

You can find more information about the concrete business scenarios where this new attribute can be used in the ticket above. Hope you liked this small addition to the framework.

A minor tweak to the DBUpdater tool: Splash screen is removed in the silent mode

Today I chatted with a customer who was having issues with the DBUpdater tool, because this console app displayed a small splash screen even if it was run in the silent mode...Sounds very familiar? Yeah, I saw this myself in the past a few times and I knew that this small icon appeared because DBUpdater setup XafApplication internally. After a quick search I found that I even logged a suggestion on removing this icon:

DBUpdater - Remove an application splash screen showing during the database

Good news is that it was not too hard for me to finally fix this today. So, in 12.2 everything will operate as you expected.

Detail form record navigation improvements in Server Mode

From http://www.devexpress.com/issue=S30968:

"We have implemented this feature in XAF starting from version 12.2 as follows:
- WinForms: By default you can navigate 50 record UP from the currently selected row and 50 records DOWN. You can control this range via the static GridListEditor.PageRowCountForServerMode property.
- ASP.NET: You can navigate only records shown on the current page.

In both WinForms and ASP.NET apps if grouping is applied, you cannot navigate records that are present in open groups only. We enjoy hearing your feedback on this feature once the next XAF version is out by the end of this year."

XPO will support OData V3

As you probably know, XPO now ships with the integrated support of the Open Data Protocol V2 (OData). XPO OData Service uses XPO classes and LinqToXpo and fully complies with associated OData standards. Any OData-compatible client can connect to this service.

See also my early post about XPO OData service: Exposing OData from an XPO data model

Today, I received a good news from our XPO labs - the next version of XPO will support OData V3 features.

BTW, if you do not yet know what to do with your OData service, feed your new DXTREME application with it;-)

Refactoring the XAF database updater functionality

These days we have been refactoring the DatabaseUpdater and ModuleUpdater classes (responsible for database schema creation, versioning and supplying initial data for your application). First, I would like to talk about the improvement described at http://www.devexpress.com/issue=S39783:

"Starting with 12.2, you can override (it is recommended for better performance) the GetModuleTypes method and create required ModuleUpdaters manually. If you do not override this method, XAF will look for ModuleUpdater descendants itself. This is required to avoid a breaking change in existing applications.
If your custom module is not supposed to provide any updates to the database, then override the method as follows:

[C#]public override IEnumerable<ModuleUpdater> GetModuleUpdaters(IObjectSpace objectSpace, Version versionFromDB) { return ModuleUpdater.EmptyModuleUpdaters; }

All standard XAF modules and templates will follow these best practices as well."

 

Second, we should also free the ORM-agnostic ModuleUpdater class from the remainder XPO stuff related to schema modifications (DeleteObjectType, DropColumn and the like). Probably, it is good to move all these methods into a helper class in DevExpress.ExpressApp.Xpo.

Yesterday, I have also fixed a small issue in the DBUpdater tool  prevented it from using a user-defined DatabaseUpdater class. I have also removed all XPO dependencies from it.

Probably, the last thing to work here is http://www.devexpress.com/issue=S136591 as it may come in handy for more user friendly splashes with progress, like the ones Adobe loves;-)